Implement the playback event handling as a system-wide multi-purpose event system. Unified mpeg.c and playback.c audio event handling. Converted ata_idle_notify to use the new event handling system also.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@16682 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 3911223..e5df04a 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -8191,29 +8191,27 @@
 </phrase>
 <phrase>
   id: LANG_SETTINGS_SAVE_FAILED
-  desc: displayed if save settings has failed
+  desc: DEPRECATED
   user:
   <source>
-    *: "Save Failed"
+    *: ""
   </source>
   <dest>
-    *: "Save Failed"
+    *: ""
   </dest>
   <voice>
-    *: "Save Failed"
+    *: ""
   </voice>
 </phrase>
 <phrase>
   id: LANG_SETTINGS_PARTITION
-  desc: if save settings has failed
+  desc: DEPRECATED
   user:
   <source>
-    *: "No partition?"
-    player: "Partition?"
+    *: ""
   </source>
   <dest>
-    *: "No partition?"
-    player: "Partition?"
+    *: ""
   </dest>
   <voice>
     *: ""
diff --git a/apps/main.c b/apps/main.c
index 5985975..5dd92e5 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -19,7 +19,6 @@
 #include "config.h"
 
 #include "ata.h"
-#include "ata_idle_notify.h"
 #include "disk.h"
 #include "fat.h"
 #include "lcd.h"
@@ -428,7 +427,6 @@
     }
 #endif
 
-    ata_idle_notify_init();
     rc = ata_init();
     if(rc)
     {
diff --git a/apps/playback.c b/apps/playback.c
index b36f68f..ee1787e 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -245,13 +245,6 @@
  */
 static bool codec_requested_stop = false;
 
-struct playback_event {
-    enum PLAYBACK_EVENT_TYPE type;
-    void (*callback)(void *data);
-};
-
-struct playback_event events[PLAYBACK_MAX_EVENTS];
-
 static size_t buffer_margin  = 0; /* Buffer margin aka anti-skip buffer (A/C-) */
 
 /* Multiple threads */
@@ -1447,51 +1440,6 @@
 
 /* --- Audio thread --- */
 
-void playback_add_event(enum PLAYBACK_EVENT_TYPE type, void (*handler))
-{
-    int i;
-    
-    /* Try to find a free slot. */
-    for (i = 0; i < PLAYBACK_MAX_EVENTS; i++)
-    {
-        if (events[i].callback == NULL)
-        {
-            events[i].type = type;
-            events[i].callback = handler;
-            return;
-        }
-    }
-    
-    panicf("playback event line full");
-}
-
-void playback_remove_event(enum PLAYBACK_EVENT_TYPE type, void (*handler))
-{
-    int i;
-    
-    for (i = 0; i < PLAYBACK_MAX_EVENTS; i++)
-    {
-        if (events[i].type == type && events[i].callback == handler)
-        {
-            events[i].callback = NULL;
-            return;
-        }
-    }
-    
-    panicf("playback event not found");
-}
-
-static void send_event(enum PLAYBACK_EVENT_TYPE type, void *data)
-{
-    int i;
-    
-    for (i = 0; i < PLAYBACK_MAX_EVENTS; i++)
-    {
-        if (events[i].type == type && events[i].callback != NULL)
-            events[i].callback(data);
-    }
-}
-
 static bool audio_have_tracks(void)
 {
     return (audio_track_count() != 0);
@@ -1783,7 +1731,7 @@
     {
         if (get_metadata(&id3, fd, trackname))
         {
-            send_event(PLAYBACK_EVENT_TRACK_BUFFER, &id3);
+            send_event(PLAYBACK_EVENT_TRACK_BUFFER, false, &id3);
             
             tracks[track_widx].id3_hid =
                 bufalloc(&id3, sizeof(struct mp3entry), TYPE_ID3);
@@ -2020,7 +1968,7 @@
     bool end_of_playlist;  /* Temporary flag, not the same as playlist_end */
 
     /* Now it's good time to send track unbuffer events. */
-    send_event(PLAYBACK_EVENT_TRACK_FINISH, &curtrack_id3);
+    send_event(PLAYBACK_EVENT_TRACK_FINISH, false, &curtrack_id3);
     
     if (dir_skip)
     {
@@ -2391,7 +2339,7 @@
         bufgetid3(prev_ti->id3_hid)->elapsed = 0;
     }
 
-    send_event(PLAYBACK_EVENT_TRACK_CHANGE, &curtrack_id3);
+    send_event(PLAYBACK_EVENT_TRACK_CHANGE, false, &curtrack_id3);
 
     track_changed = true;
     playlist_update_resume_info(audio_current_track());
diff --git a/apps/playback.h b/apps/playback.h
index 14ba35c..0b78eb3 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -24,6 +24,7 @@
 
 #include "id3.h"
 #include "mp3data.h"
+#include "events.h"
 
 #define CODEC_IDX_AUDIO     0
 #define CODEC_IDX_VOICE     1
@@ -39,13 +40,6 @@
 
 #define MAX_TRACK_MASK  (MAX_TRACK-1)
 
-#define PLAYBACK_MAX_EVENTS  4
-enum PLAYBACK_EVENT_TYPE {
-    PLAYBACK_EVENT_TRACK_BUFFER,
-    PLAYBACK_EVENT_TRACK_FINISH,
-    PLAYBACK_EVENT_TRACK_CHANGE,
-};
-
 /* Functions */
 const char * get_codec_filename(int cod_spec);
 void voice_wait(void);
@@ -53,13 +47,7 @@
 #if CONFIG_CODEC == SWCODEC /* This #ifdef is better here than gui/gwps.c */
 extern void audio_next_dir(void);
 extern void audio_prev_dir(void);
-void playback_add_event(enum PLAYBACK_EVENT_TYPE type, void (*handler));
-void playback_remove_event(enum PLAYBACK_EVENT_TYPE type, void (*handler));
 #else
-/* Really, should get rid of these HWCODEC api definitions here. */
-void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3));
-void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3));
-void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3));
 # define audio_next_dir() 
 #define audio_prev_dir()
 #endif
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
index 16869bf..2f60e85 100644
--- a/apps/scrobbler.c
+++ b/apps/scrobbler.c
@@ -183,7 +183,10 @@
     } else {
         cache_pos++;
         if (!scrobbler_ata_callback)
-            scrobbler_ata_callback = register_ata_idle_func(scrobbler_flush_callback);
+        {
+            register_ata_idle_func(scrobbler_flush_callback);
+            scrobbler_ata_callback = true;
+        }
     }
 
 }
@@ -224,11 +227,7 @@
 
     scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
 
-#if CONFIG_CODEC == SWCODEC
-    playback_add_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
-#else
-    audio_set_track_changed_event(&scrobbler_change_event);
-#endif
+    add_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
     cache_pos = 0;
     pending = false;
     scrobbler_initialised = true;
@@ -263,11 +262,7 @@
 
     if (scrobbler_initialised)
     {
-#if CONFIG_CODEC == SWCODEC
-        playback_remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
-#else
-        audio_set_track_changed_event(NULL);
-#endif
+        remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
         scrobbler_initialised = false;
     }
 }
diff --git a/apps/settings.c b/apps/settings.c
index 27832b4..7eec15b 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -601,25 +601,7 @@
        target doesnt have rtc ram */
     write_nvram_data(nvram_buffer,NVRAM_BLOCK_SIZE);
 #endif
-    if(!register_ata_idle_func(flush_config_block_callback))
-    {
-        int i;
-        FOR_NB_SCREENS(i)
-        {
-            screens[i].clear_display();
-#ifdef HAVE_LCD_CHARCELLS
-            screens[i].puts(0, 0, str(LANG_SETTINGS_SAVE_FAILED));
-            screens[i].puts(0, 1, str(LANG_SETTINGS_PARTITION));
-#else
-            screens[i].puts(4, 2, str(LANG_SETTINGS_SAVE_FAILED));
-            screens[i].puts(2, 4, str(LANG_SETTINGS_PARTITION));
-            screens[i].update();
-#endif
-        }
-        cond_talk_ids_fq(LANG_SETTINGS_SAVE_FAILED);
-        sleep(HZ*2);
-        return -1;
-    }
+    register_ata_idle_func(flush_config_block_callback);
     return 0;
 }
 bool settings_save_config(int options)
diff --git a/apps/tagtree.c b/apps/tagtree.c
index c5e3ebf..4572204 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -924,14 +924,9 @@
         root_menu = 0;
     
     uniqbuf = buffer_alloc(UNIQBUF_SIZE);
-#if CONFIG_CODEC == SWCODEC
-    playback_add_event(PLAYBACK_EVENT_TRACK_BUFFER, tagtree_buffer_event);
-    playback_add_event(PLAYBACK_EVENT_TRACK_FINISH, tagtree_track_finish_event);
-#else
-    audio_set_track_buffer_event(tagtree_buffer_event);
-    audio_set_track_unbuffer_event(tagtree_track_finish_event);
-#endif
-    
+
+    add_event(PLAYBACK_EVENT_TRACK_BUFFER, tagtree_buffer_event);
+    add_event(PLAYBACK_EVENT_TRACK_FINISH, tagtree_track_finish_event);
 }
 
 static bool show_search_progress(bool init, int count)
diff --git a/firmware/SOURCES b/firmware/SOURCES
index b5906d4..ea8ce46 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1,4 +1,5 @@
 ata_idle_notify.c
+events.c
 backlight.c
 buffer.c
 id3.c
diff --git a/firmware/ata_idle_notify.c b/firmware/ata_idle_notify.c
index 1fc6605..a97c353 100644
--- a/firmware/ata_idle_notify.c
+++ b/firmware/ata_idle_notify.c
@@ -23,60 +23,31 @@
 #include "kernel.h"
 #include "string.h"
 
-#if USING_ATA_CALLBACK
-static ata_idle_notify ata_idle_notify_funcs[MAX_ATA_CALLBACKS];
-static int ata_callback_count = 0;
-#endif
-
-
-bool register_ata_idle_func(ata_idle_notify function)
+void register_ata_idle_func(ata_idle_notify function)
 {
 #if USING_ATA_CALLBACK
-    int i;
-    if (ata_callback_count >= MAX_ATA_CALLBACKS)
-        return false;
-    for (i=0; i<MAX_ATA_CALLBACKS; i++)
-    {
-        if (ata_idle_notify_funcs[i] == NULL)
-        {
-            ata_idle_notify_funcs[i] = function;
-            ata_callback_count++;
-            return true;
-        }
-        else if (ata_idle_notify_funcs[i] == function)
-            return true;
-    }
-    return false;
+    add_event(DISK_EVENT_SPINUP, function);
 #else
     function(); /* just call the function now */
 /* this _may_ cause problems later if the calling function
    sets a variable expecting the callback to unset it, because
    the callback will be run before this function exits, so before the var is set */
-    return true;
 #endif
 }
 
 #if USING_ATA_CALLBACK
 void unregister_ata_idle_func(ata_idle_notify func, bool run)
 {
-    int i;
-    for (i=0; i<MAX_ATA_CALLBACKS; i++)
-    {
-        if (ata_idle_notify_funcs[i] == func)
-        {
-            ata_idle_notify_funcs[i] = NULL;
-            ata_callback_count--;
-            if (run) func();
-        }
-    }
-    return;
+    remove_event(DISK_EVENT_SPINUP, func);
+    
+    if (run)
+        func();
 }
 
 bool call_ata_idle_notifys(bool force)
 {
-    int i;
     static int lock_until = 0;
-    ata_idle_notify function;
+
     if (!force)
     {
         if (TIME_BEFORE(current_tick,lock_until) )
@@ -84,22 +55,8 @@
     }
     lock_until = current_tick + 30*HZ;
 
-    for (i = 0; i < MAX_ATA_CALLBACKS; i++)
-    {
-        if (ata_idle_notify_funcs[i])
-        {
-            function = ata_idle_notify_funcs[i];
-            ata_idle_notify_funcs[i] = NULL;
-            function();
-            ata_callback_count--;
-        }
-    }
+    send_event(DISK_EVENT_SPINUP, true, NULL);
+    
     return true;
 }
-
-void ata_idle_notify_init(void)
-{
-    ata_callback_count = 0;
-    memset(ata_idle_notify_funcs, 0, sizeof(ata_idle_notify_funcs));
-}
 #endif
diff --git a/firmware/events.c b/firmware/events.c
new file mode 100644
index 0000000..eaf2e5c
--- /dev/null
+++ b/firmware/events.c
@@ -0,0 +1,88 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2008 by Miika Pekkarinen
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include "events.h"
+#include "panic.h"
+
+struct sysevent {
+    unsigned short id;
+    void (*callback)(void *data);
+};
+
+struct sysevent events[MAX_SYS_EVENTS];
+
+bool add_event(unsigned short id, void (*handler))
+{
+    int i;
+    
+    /* Chcek if the event already exists. */
+    for (i = 0; i < MAX_SYS_EVENTS; i++)
+    {
+        if (events[i].callback == handler && events[i].id == id)
+            return false;
+    }
+    
+    /* Try to find a free slot. */
+    for (i = 0; i < MAX_SYS_EVENTS; i++)
+    {
+        if (events[i].callback == NULL)
+        {
+            events[i].id = id;
+            events[i].callback = handler;
+            return true;
+        }
+    }
+    
+    panicf("event line full");
+    return false;
+}
+
+void remove_event(unsigned short id, void (*handler))
+{
+    int i;
+    
+    for (i = 0; i < MAX_SYS_EVENTS; i++)
+    {
+        if (events[i].id == id && events[i].callback == handler)
+        {
+            events[i].callback = NULL;
+            return;
+        }
+    }
+    
+    panicf("event not found");
+}
+
+void send_event(unsigned short id, bool oneshot, void *data)
+{
+    int i;
+    
+    for (i = 0; i < MAX_SYS_EVENTS; i++)
+    {
+        if (events[i].id == id && events[i].callback != NULL)
+        {
+            events[i].callback(data);
+            
+            if (oneshot)
+                events[i].callback = NULL;
+        }
+    }
+}
+
diff --git a/firmware/export/ata_idle_notify.h b/firmware/export/ata_idle_notify.h
index ee825c9..1dda3e1 100644
--- a/firmware/export/ata_idle_notify.h
+++ b/firmware/export/ata_idle_notify.h
@@ -19,7 +19,9 @@
 #ifndef __ATACALLBACK_H__
 #define __ATACALLBACK_H__
 
+
 #include <stdbool.h>
+#include "events.h"
 
 #if 0
                 NOTE: ata_idle_nofity usage notes..
@@ -34,15 +36,17 @@
         5) Dont Panic!
 #endif
 
+enum {
+    DISK_EVENT_SPINUP = (EVENT_CLASS_DISK|1),
+};
+
 #define USING_ATA_CALLBACK  !defined(SIMULATOR)             \
                             && !defined(HAVE_FLASH_DISK)
 
-#define MAX_ATA_CALLBACKS 5
 typedef bool (*ata_idle_notify)(void);
 
-extern bool register_ata_idle_func(ata_idle_notify function);
+extern void register_ata_idle_func(ata_idle_notify function);
 #if USING_ATA_CALLBACK
-extern void ata_idle_notify_init(void);
 extern void unregister_ata_idle_func(ata_idle_notify function, bool run);
 extern bool call_ata_idle_notifys(bool force);
 #else
diff --git a/firmware/export/events.h b/firmware/export/events.h
new file mode 100644
index 0000000..b27b5de
--- /dev/null
+++ b/firmware/export/events.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2008 by Miika Pekkarinen
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _EVENTS_H
+#define _EVENTS_H
+
+#include <stdbool.h>
+
+#define MAX_SYS_EVENTS 10
+
+/**
+ * High nibble = Event class definition
+ * Low nibble  = Event ID
+ */
+
+#define EVENT_CLASS_DISK       0x0100
+#define EVENT_CLASS_PLAYBACK   0x0200
+
+/**
+ * Because same playback events are used in mpeg.c and playback.c, define
+ * them here to prevent cluttering and ifdefs.
+ */
+enum {
+    PLAYBACK_EVENT_TRACK_BUFFER = (EVENT_CLASS_PLAYBACK|1),
+    PLAYBACK_EVENT_TRACK_FINISH,
+    PLAYBACK_EVENT_TRACK_CHANGE,
+};
+
+
+bool add_event(unsigned short id, void (*handler));
+void remove_event(unsigned short id, void (*handler));
+void send_event(unsigned short id, bool oneshot, void *data);
+
+#endif
+
diff --git a/firmware/export/mpeg.h b/firmware/export/mpeg.h
index 3e36c44..0a9d62c 100644
--- a/firmware/export/mpeg.h
+++ b/firmware/export/mpeg.h
@@ -21,6 +21,7 @@
 
 #include <stdbool.h>
 #include "id3.h"
+#include "events.h"
 
 #define MPEG_SWAP_CHUNKSIZE  0x2000
 #define MPEG_HIGH_WATER  2 /* We leave 2 bytes empty because otherwise we
diff --git a/firmware/mpeg.c b/firmware/mpeg.c
index 693e2df..9023c30 100644
--- a/firmware/mpeg.c
+++ b/firmware/mpeg.c
@@ -100,7 +100,6 @@
     struct mp3entry id3;
     int mempos;
     int load_ahead_index;
-    bool event_sent;
 };
 
 static struct trackdata trackdata[MAX_TRACK_ENTRIES];
@@ -116,11 +115,6 @@
 static int track_write_idx = 0;
 #endif /* !SIMULATOR */
 
-/* Callback function to call when current track has really changed. */
-void (*track_changed_callback)(struct mp3entry *id3) = NULL;
-void (*track_buffer_callback)(struct mp3entry *id3) = NULL;
-void (*track_unbuffer_callback)(struct mp3entry *id3) = NULL;
-
 /* Cuesheet callback */
 static bool (*cuesheet_callback)(const char *filename) = NULL;
 
@@ -475,21 +469,6 @@
 #endif /* !SIMULATOR */
 }
 
-void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3))
-{
-    track_buffer_callback = handler;
-}
-
-void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3))
-{
-    track_unbuffer_callback = handler;
-}
-
-void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3))
-{
-    track_changed_callback = handler;
-}
-
 void audio_set_cuesheet_callback(bool (*handler)(const char *filename))
 {
     cuesheet_callback = handler;
@@ -506,12 +485,7 @@
     for (i = 0; i < numentries; i++)
     {
         /* Send an event to notify that track has finished. */
-        if (trackdata[cur_idx].event_sent)
-        {
-            if (track_unbuffer_callback)
-                track_unbuffer_callback(&trackdata[cur_idx].id3);
-            trackdata[cur_idx].event_sent = false;
-        }
+        send_event(PLAYBACK_EVENT_TRACK_FINISH, false, &trackdata[cur_idx].id3);
         cur_idx = (cur_idx + 1) & MAX_TRACK_ENTRIES_MASK;
     }
 }
@@ -525,12 +499,7 @@
 
     for (i = 0; i < numentries; i++)
     {
-        if (!trackdata[cur_idx].event_sent)
-        {
-            if (track_buffer_callback)
-                track_buffer_callback(&trackdata[cur_idx].id3);
-            trackdata[cur_idx].event_sent = true;
-        }
+        send_event(PLAYBACK_EVENT_TRACK_BUFFER, false, &trackdata[cur_idx].id3);
         cur_idx = (cur_idx + 1) & MAX_TRACK_ENTRIES_MASK;
     }
 }
@@ -1080,8 +1049,7 @@
     if (num_tracks_in_memory() > 0)
     {
         remove_current_tag();
-        if (track_changed_callback)
-            track_changed_callback(audio_current_track());
+        send_event(PLAYBACK_EVENT_TRACK_CHANGE, false, audio_current_track());
         update_playlist();
     }
 
@@ -1134,8 +1102,7 @@
                 if (play_pending_track_change)
                 {
                     play_pending_track_change = false;
-                    if(track_changed_callback)
-                        track_changed_callback(audio_current_track());
+                    send_event(PLAYBACK_EVENT_TRACK_CHANGE, false, audio_current_track());
                 }
                 play_pending = false;
             }