Back to index

gcompris  8.2.2
soundutil.c
Go to the documentation of this file.
00001 /* gcompris - soundutil.c
00002  *
00003  * Copyright (C) 2002 Pascal Georges
00004  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU General Public License as published by
00007  *   the Free Software Foundation; either version 2 of the License, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details.
00014  *
00015  *   You should have received a copy of the GNU General Public License
00016  *   along with this program; if not, write to the Free Software
00017  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00020 #include "string.h"
00021 
00022 #ifdef __APPLE__
00023 #   include <sys/types.h>
00024 #endif
00025 #include "gcompris.h"
00026 #include <signal.h>
00027 #include <glib.h>
00028 
00029 static GList  *pending_queue = NULL;
00030 static int     sound_policy;
00031 static gboolean       music_paused = FALSE;
00032 static gboolean       sound_closed = FALSE;
00033 
00034 /* mutex */
00035 GMutex        *lock = NULL;
00036 GMutex        *lock_music = NULL;
00037 GMutex        *lock_fx = NULL;
00038 GCond         *cond = NULL;
00039 
00040 /* Singleton */
00041 static guint   sound_init = 0;
00042 
00043 /* Forward function declarations */
00044 GThread              *thread_scheduler_fx, *thread_scheduler_music;
00045 
00046 static void   *thread_play_ogg (gchar *file);
00047 static char   *get_next_sound_to_play( );
00048 
00049 static gpointer  scheduler_fx (gpointer user_data);
00050 static gpointer  scheduler_music (gpointer user_data);
00051 
00052 /* sound control */
00053 GObject *gc_sound_controller = NULL;
00054 void gc_sound_callback(GcomprisSound *ctl, gchar *file, gpointer user_data);
00055 GHashTable *sound_callbacks = NULL;
00056 
00057 /* =====================================================================
00058  *
00059  * =====================================================================*/
00060 void
00061 gc_sound_init()
00062 {
00063 
00064   /* Check to run the init once only */
00065   if(sound_init == 1)
00066     {
00067       if(sound_closed == TRUE)
00068        gc_sound_reopen();
00069 
00070          return;
00071     }
00072   sound_init = 1;
00073 
00074   gc_sound_controller = g_object_new (GCOMPRIS_SOUND_TYPE, NULL);
00075 
00076   g_signal_connect( gc_sound_controller,
00077                   "sound-played",
00078                   (GCallback) gc_sound_callback,
00079                   NULL);
00080 
00081   g_assert( gc_sound_controller != NULL );
00082 
00083   /* Initialize the thread system */
00084   if (!g_thread_supported ()) g_thread_init (NULL);
00085 
00086   lock = g_mutex_new ();
00087   lock_music = g_mutex_new ();
00088   lock_fx = g_mutex_new ();
00089   cond = g_cond_new ();
00090 
00091   sound_policy = PLAY_AFTER_CURRENT;
00092 
00093   if(sdlplayer_init()!=0) {
00094     /* Sound init failed. Desactivate the sound */
00095     gc_prop_get()->music = 0;
00096     gc_prop_get()->fx    = 0;
00097     return;
00098   }
00099 
00100   thread_scheduler_fx = g_thread_create((GThreadFunc)scheduler_fx, NULL, FALSE, NULL);
00101   if (thread_scheduler_fx == NULL)
00102     perror("create failed for fx scheduler");
00103 
00104   thread_scheduler_music = g_thread_create((GThreadFunc)scheduler_music, NULL, FALSE, NULL);
00105   if (thread_scheduler_music == NULL)
00106     perror("create failed for music scheduler");
00107 
00108 }
00109 
00110 void
00111 gc_sound_close()
00112 {
00113   if ( !sound_closed )
00114     {
00115       sdlplayer_halt_music();
00116       sdlplayer_halt_fx();
00117       g_mutex_lock(lock_fx);
00118       g_mutex_lock(lock_music);
00119       sdlplayer_close();
00120       sound_closed = TRUE;
00121       music_paused = FALSE;
00122     }
00123 }
00124 
00125 void
00126 gc_sound_reopen()
00127 {
00128   if (sound_closed)
00129     {
00130       sdlplayer_reopen();
00131       g_mutex_unlock(lock_fx);
00132       g_mutex_unlock(lock_music);
00133       sound_closed = FALSE;
00134       music_paused = FALSE;
00135     }
00136 }
00137 
00138 void
00139 gc_sound_pause()
00140 {
00141   sdlplayer_pause_music();
00142   music_paused = TRUE;
00143 }
00144 
00145 void
00146 gc_sound_resume()
00147 {
00148   sdlplayer_resume_music();
00149   music_paused = FALSE;
00150 }
00151 
00152 /* =====================================================================
00153  *
00154  * =====================================================================*/
00155 void
00156 gc_sound_policy_set(int policy)
00157 {
00158   switch (policy)
00159     {
00160     case PLAY_ONLY_IF_IDLE : sound_policy = PLAY_ONLY_IF_IDLE; break;
00161     case PLAY_AFTER_CURRENT : sound_policy = PLAY_AFTER_CURRENT; break;
00162     case PLAY_AND_INTERRUPT : sound_policy = PLAY_AND_INTERRUPT; break;
00163     default : sound_policy = PLAY_AFTER_CURRENT;
00164     }
00165 }
00166 /* =====================================================================
00167  *
00168  * =====================================================================*/
00169 int
00170 gc_sound_policy_get()
00171 {
00172   return sound_policy;
00173 }
00174 
00175 /* =====================================================================
00176  * Thread scheduler background :
00177  *     - launches a single thread for playing and play any file found
00178  *        in the gcompris music directory
00179  ======================================================================*/
00180 static gpointer
00181 scheduler_music (gpointer user_data)
00182 {
00183   GcomprisProperties *properties = gc_prop_get();
00184   gint i;
00185   gchar *str;
00186   gchar *music_dir;
00187   GSList *musiclist = NULL;
00188   GDir *dir;
00189   const gchar *one_dirent;
00190 
00191   /* Sleep to let gcompris intialisation and intro music to complete */
00192   g_usleep(25000000);
00193 
00194   /* Load the Music directory file names */
00195   music_dir = g_strconcat(properties->package_data_dir, "/music/background", NULL);
00196 
00197   dir = g_dir_open(music_dir, 0, NULL);
00198 
00199   if (!dir) {
00200     g_warning ("Couldn't open music dir: %s", music_dir);
00201     g_free(music_dir);
00202     return NULL;
00203   }
00204 
00205   /* Fill up the music list */
00206   while((one_dirent = g_dir_read_name(dir)) != NULL)
00207     {
00208       if (g_str_has_suffix(one_dirent, ".ogg"))
00209        {
00210          str = g_strdup_printf("%s/%s", music_dir, one_dirent);
00211          musiclist = g_slist_insert (musiclist, str, RAND(0, g_slist_length(musiclist)));
00212        }
00213     }
00214   g_dir_close(dir);
00215 
00216   /* No music no play */
00217   if(g_slist_length(musiclist)==0)
00218     {
00219       g_free(music_dir);
00220       return NULL;
00221     }
00222 
00223   /* Now loop over all our music files */
00224   while (TRUE)
00225     {
00226       for(i=0; i<g_slist_length(musiclist); i++)
00227        {
00228          /* Music can be disabled at any time */
00229          while(!gc_prop_get()->music || music_paused)
00230            g_usleep(1000000);
00231 
00232          /* WARNING Displaying stuff in a thread seems to make gcompris unstable */
00233          /*     display_ogg_file_credits((char *)g_list_nth_data(musiclist, i)); */
00234          //     if(decode_ogg_file((char *)g_list_nth_data(musiclist, i))!=0)
00235          g_mutex_lock(lock_music);
00236          if(sdlplayer_music((char *)g_slist_nth_data(musiclist, i), 128)!=0){
00237            g_warning("sdlplayer_music failed, try again in 5 seconds");
00238            g_usleep(5000000);
00239          }
00240          g_mutex_unlock(lock_music);
00241 
00242        }
00243     }
00244 
00245   /* Never happen */
00246   g_slist_free(musiclist);
00247   g_warning( "The background thread music is stopped now. "\
00248             "The files in %s are not ogg vorbis OR the sound output failed",
00249             music_dir);
00250   g_free(music_dir);
00251   return NULL;
00252 }
00253 /* =====================================================================
00254  * Thread scheduler :
00255  *     - launches a single thread for playing a file
00256  *     - joins the previous thread at its end
00257  *     -      then launches another thread if some sounds are pending
00258  *     -      the thread never ends
00259  ======================================================================*/
00260 static gpointer
00261 scheduler_fx (gpointer user_data)
00262 {
00263   char *sound = NULL;
00264 
00265   while (TRUE)
00266     {
00267       if ( ( sound = get_next_sound_to_play( ) ) != NULL )
00268        {
00269          thread_play_ogg(sound);
00270          g_free(sound);
00271        }
00272       else
00273        {
00274          g_mutex_lock (lock);
00275          g_cond_wait (cond, lock);
00276          g_mutex_unlock (lock);
00277        }
00278     }
00279   return NULL;
00280 }
00281 /* =====================================================================
00282  * Thread function for playing a single file
00283  ======================================================================*/
00284 static void*
00285 thread_play_ogg (gchar *file)
00286 {
00287   gchar *absolute_file;
00288 
00289   g_warning("  Thread_play_ogg %s", file);
00290 
00291   absolute_file = gc_file_find_absolute(file);
00292 
00293   if (!absolute_file)
00294     return NULL;
00295 
00296   g_warning("   Calling gcompris internal sdlplayer_file (%s)", absolute_file);
00297   g_mutex_lock(lock_fx);
00298   sdlplayer_fx(absolute_file, 128);
00299   g_mutex_unlock(lock_fx);
00300   g_signal_emit (gc_sound_controller,
00301                GCOMPRIS_SOUND_GET_CLASS (gc_sound_controller)->sound_played_signal_id,
00302                0 /* details */,
00303                g_strdup(file));
00304   g_warning("  sdlplayer_fx(%s) ended.", absolute_file);
00305 
00306   g_free(absolute_file);
00307 
00308   return NULL;
00309 }
00310 
00311 /* =====================================================================
00312  * Returns the next sound play, or NULL if there is no
00313  ======================================================================*/
00314 static char*
00315 get_next_sound_to_play( )
00316 {
00317   char* tmpSound = NULL;
00318 
00319   g_mutex_lock (lock);
00320 
00321   if ( g_list_length(pending_queue) > 0 )
00322     {
00323       tmpSound = g_list_nth_data( pending_queue, 0 );
00324       pending_queue = g_list_remove( pending_queue, tmpSound );
00325       g_warning( "... get_next_sound_to_play : %s\n", tmpSound );
00326     }
00327 
00328   g_mutex_unlock (lock);
00329 
00330   return tmpSound;
00331 }
00332 
00333 /* =====================================================================
00334  * Play a OGG sound file.
00335  * gc_sound_play_ogg function to process the sounds.
00336  ======================================================================*/
00337 void
00338 gc_sound_play_ogg_cb(const gchar *file, GcomprisSoundCallback cb)
00339 {
00340 
00341   g_assert ( cb != NULL);
00342 
00343   /* g_intern_string is in 2.10 */
00344   const gchar *intern_file = g_quark_to_string( g_quark_from_string(file));
00345 
00346   if (!sound_callbacks)
00347     sound_callbacks = g_hash_table_new_full (g_str_hash,
00348                                        g_str_equal,
00349                                        NULL,
00350                                        NULL);
00351 
00352   /* i suppose there will not be two call of that function with same sound file before sound is played */
00353   g_hash_table_replace (sound_callbacks,
00354                      (gpointer)intern_file,
00355                      cb);
00356   gc_sound_play_ogg(intern_file, NULL);
00357 }
00358 
00359 /* =====================================================================
00360  * Play a list of OGG sound files. The list must be NULL terminated
00361  * This function wraps the var args into a GList and call the
00362  * gc_sound_play_ogg_list function to process the sounds.
00363  ======================================================================*/
00364 void
00365 gc_sound_play_ogg(const gchar *sound, ...)
00366 {
00367   va_list ap;
00368   char* tmp = NULL;
00369   GList* list = NULL;
00370 
00371   if(!sound || !gc_prop_get()->fx)
00372     return;
00373 
00374   list = g_list_append(list, (gpointer)sound);
00375 
00376   g_warning("Adding %s in the play list queue\n", sound);
00377 
00378   va_start( ap, sound);
00379   while( (tmp = va_arg (ap, char *)))
00380     {
00381       list = g_list_append(list, (gpointer)tmp);
00382     }
00383   va_end(ap);
00384 
00385   gc_sound_play_ogg_list( list );
00386 
00387   g_list_free(list);
00388 }
00389 
00390 /* =====================================================================
00391  * Play a list of OGG sound files.
00392  * The given ogg files will be first tested as a locale dependant sound file:
00393  * sounds/<current gcompris locale>/<sound>
00394  * If it doesn't exists, then the test is done with a music file:
00395  * music/<sound>
00396  =====================================================================*/
00397 void free_string(gpointer data,  gpointer user_data)
00398 {
00399   g_free(data);
00400 }
00401 void
00402 gc_sound_play_ogg_list( GList* files )
00403 {
00404   GList* list;
00405   char* tmpSound = NULL;
00406 
00407   if ( !gc_prop_get()->fx )
00408     return;
00409 
00410   if (        sound_policy == PLAY_ONLY_IF_IDLE &&
00411         g_list_length( pending_queue ) > 0 )
00412     return;
00413 
00414   if ( sound_policy == PLAY_AND_INTERRUPT ) {
00415     g_warning("halt music");
00416     while ( g_list_length(pending_queue) > 0 )
00417     {
00418       tmpSound = g_list_nth_data( pending_queue, 0 );
00419       pending_queue = g_list_remove( pending_queue, tmpSound );
00420       g_free(tmpSound);
00421     }
00422     sdlplayer_halt_fx();
00423   }
00424 
00425   g_mutex_lock (lock);
00426 
00427   list = g_list_first( files );
00428   while( list!=NULL )
00429     {
00430       if (g_list_length(pending_queue) < MAX_QUEUE_LENGTH)
00431        {
00432          pending_queue = g_list_append(pending_queue, g_strdup( (gchar*)(list->data) ));
00433        }
00434       list = g_list_next(list);
00435     }
00436 
00437   g_mutex_unlock (lock);
00438 
00439   // Tell the scheduler to check for new sounds to play
00440   g_warning("Tell the scheduler to check for new sounds to play\n");
00441   g_cond_signal (cond);
00442 
00443 }
00444 
00453 gchar *
00454 gc_sound_alphabet(gchar *chars)
00455 {
00456   gchar *next, *str, *prev, *result;
00457   gint i;
00458   gint length;
00459   gunichar next_unichar;
00460 
00461   length = g_utf8_strlen(chars, -1);
00462 
00463   next = chars;
00464   result = NULL;
00465 
00466   for (i=0; i < length; i++) {
00467     next_unichar = g_utf8_get_char(next);
00468     str = g_strdup_printf("U%.4X",(gint32) g_unichar_tolower(next_unichar));
00469     prev = result;
00470     if (prev)
00471       result = g_strconcat( prev, str, NULL);
00472     else
00473       result = g_strdup(str);
00474 
00475     g_free(str);
00476     g_free(prev);
00477     next = g_utf8_next_char(next);
00478   }
00479 
00480   gchar *result2 = g_strdup_printf("%s.ogg",result);
00481   g_free(result);
00482 
00483   return result2;
00484 }
00485 
00486 
00487 void gc_sound_callback(GcomprisSound *ctl, gchar *file, gpointer user_data)
00488 {
00489   GcomprisSoundCallback cb;
00490 
00491   if (!sound_callbacks)
00492     return;
00493 
00494   cb = g_hash_table_lookup (sound_callbacks, file);
00495 
00496   if (cb){
00497     g_warning("calling callback for %s", file);
00498     cb(file);
00499   }
00500   else
00501     g_warning("%s has no callback", file);
00502   g_hash_table_remove(sound_callbacks, file);
00503 
00504 }
00505 
00506 /*************************************/
00507 /* GObject control sound             */
00508 struct _GcomprisSoundPrivate
00509 {
00510 };
00511 
00512 #include "gcompris-marshal.h"
00513 
00514 static void
00515 gc_sound_instance_init (GTypeInstance   *instance,
00516                            gpointer         g_class)
00517 {
00518         GcomprisSound *self = (GcomprisSound *)instance;
00519         self->private = g_new (GcomprisSoundPrivate, 1);
00520 }
00521 
00522 static void
00523 default_sound_played_signal_handler (GcomprisSound *obj, gchar *file, gpointer user_data)
00524 {
00525         /* Here, we trigger the real file write. */
00526         g_warning ("sound_played: %s\n", file);
00527 }
00528 
00529 static void
00530 gc_sound_class_init (gpointer g_class,
00531                         gpointer g_class_data)
00532 {
00533         GcomprisSoundClass *klass = GCOMPRIS_SOUND_CLASS (g_class);
00534 
00535        klass->sound_played = default_sound_played_signal_handler;
00536 
00537         klass->sound_played_signal_id =
00538                 g_signal_new ("sound-played",
00539                            G_TYPE_FROM_CLASS (g_class),
00540                            G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
00541                            G_STRUCT_OFFSET (GcomprisSoundClass, sound_played),
00542                            NULL /* accumulator */,
00543                            NULL /* accu_data */,
00544                            gnome_canvas_marshal_VOID__POINTER,
00545                            G_TYPE_NONE /* return_type */,
00546                            1     /* n_params */,
00547                            G_TYPE_POINTER  /* param_types */);
00548 
00549 }
00550 
00551 GType gc_sound_get_type (void)
00552 {
00553         static GType type = 0;
00554         if (type == 0) {
00555                 static const GTypeInfo info = {
00556                         sizeof (GcomprisSoundClass),
00557                         NULL,   /* base_init */
00558                         NULL,   /* base_finalize */
00559                         gc_sound_class_init,   /* class_init */
00560                         NULL,   /* class_finalize */
00561                         NULL,   /* class_data */
00562                         sizeof (GcomprisSound),
00563                         0,      /* n_preallocs */
00564                         gc_sound_instance_init    /* instance_init */
00565                 };
00566                 type = g_type_register_static (G_TYPE_OBJECT,
00567                                                "GcomprisSoundType",
00568                                                &info, 0);
00569         }
00570         return type;
00571 }