Hello,

I'm working on a Nokia N900 port of rockbox. Attached you'll find
my current code. It's working already quite nice :)

Some features:
- Gstreamer based audio backend
- Stop playback on incoming calls
- Playback of music in "silent mode" (this was a tough one)
- Battery levels and runtime estimation
- Suspend screen updates if phone display is off
  or the application is not in focus
- Adjust rockbox volume if the volume is modified from external
- Assembler optimized playback

The code is still a bit messy. I'm currently cleaning it up
for flyspray submission. Things on my todo list:
- Create something like a PLATFORM_MAEMO define
  and make the N900 support optional.
- Use only one thread for N900 specific dbus communication
- Move gstreamer backend from pcm-sdl.c to an own pcm-gstreamer.c
- Remove "TOMJ" debug output messages

The improved SDL screen update code is already in FS#11834.

On that note: Happy X-Mas.

Cheers,
Thomas
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 72b0217..bb55a7f 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -39,6 +39,7 @@ target/hosted/sdl/lcd-sdl.c
 target/hosted/sdl/system-sdl.c
 target/hosted/sdl/thread-sdl.c
 target/hosted/sdl/timer-sdl.c
+target/hosted/sdl/n900-osso.c
 #ifdef HAVE_TOUCHSCREEN
 target/hosted/sdl/key_to_touch-sdl.c
 #endif
diff --git a/firmware/drivers/audio/sdl.c b/firmware/drivers/audio/sdl.c
index 7d6d745..01d7827 100644
--- a/firmware/drivers/audio/sdl.c
+++ b/firmware/drivers/audio/sdl.c
@@ -34,8 +34,7 @@ extern void pcm_set_mixer_volume(int);
 void audiohw_set_volume(int volume)
 {
 #if CONFIG_CODEC == SWCODEC
-    pcm_set_mixer_volume(
-        SDL_MIX_MAXVOLUME * ((volume - VOLUME_MIN) / 10) / (VOLUME_RANGE / 10));
+    pcm_set_mixer_volume(volume);
 #else
     (void)volume;
 #endif
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 8e9afe5..363c21d 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -516,6 +516,10 @@ Lyre prototype 1 */
 #define ARM_ARCH 4 /* ARMv4 */
 #endif
 
+/* N900 hack: Enable ASM optimizations */
+#define CPU_ARM
+#define ARM_ARCH 8
+
 #if (CONFIG_CPU == JZ4732)
 #define CPU_MIPS 32
 #endif
diff --git a/firmware/target/hosted/sdl/button-sdl.c b/firmware/target/hosted/sdl/button-sdl.c
index 3321a01..fe3f964 100644
--- a/firmware/target/hosted/sdl/button-sdl.c
+++ b/firmware/target/hosted/sdl/button-sdl.c
@@ -34,6 +34,7 @@
 #include "sim_tasks.h"
 #include "buttonmap.h"
 #include "debug.h"
+#include "n900-osso.h"
 
 #ifdef HAVE_TOUCHSCREEN
 #include "touchscreen.h"
@@ -209,11 +210,34 @@ static void mouse_event(SDL_MouseButtonEvent *event, bool button_up)
 
 static bool event_handler(SDL_Event *event)
 {
+    SDLKey my_key;
+
     switch(event->type)
     {
+    case SDL_ACTIVEEVENT:
+        if (event->active.state & SDL_APPINPUTFOCUS)
+        {
+            if (event->active.gain == 1)
+                n900_has_input_focus = 1;
+            else
+                n900_has_input_focus = 0;
+
+            printf("TOMJ: N900 input focus: %d\n", n900_has_input_focus);
+        }
+        break;
     case SDL_KEYDOWN:
     case SDL_KEYUP:
-        button_event(event->key.keysym.sym, event->type == SDL_KEYDOWN);
+        /* Hack for german N900: Looks like SDL doesn't do the cursor mapping properly. Grrr */
+        my_key = event->key.keysym.sym;
+        if (event->key.keysym.mod & KMOD_MODE)
+        {
+            if (my_key == SDLK_LEFT)
+                my_key = SDLK_UP;
+            else if (my_key == SDLK_RIGHT)
+                my_key = SDLK_DOWN;
+        }
+
+        button_event(my_key, event->type == SDL_KEYDOWN);
         break;
 #ifdef HAVE_TOUCHSCREEN
     case SDL_MOUSEMOTION:
diff --git a/firmware/target/hosted/sdl/lcd-bitmap.c b/firmware/target/hosted/sdl/lcd-bitmap.c
index 7058b26..4da2d4d 100644
--- a/firmware/target/hosted/sdl/lcd-bitmap.c
+++ b/firmware/target/hosted/sdl/lcd-bitmap.c
@@ -22,11 +22,15 @@
 #include "debug.h"
 #include "sim-ui-defines.h"
 #include "system.h"
+#include "n900-osso.h"
 #include "lcd-sdl.h"
 #include "screendump.h"
 
 SDL_Surface* lcd_surface;
 
+Uint32 fps_starttime = 0;
+Uint32 framecount = 0;
+
 #if LCD_DEPTH <= 8
 #ifdef HAVE_BACKLIGHT
 SDL_Color lcd_bl_color_dark    = {RED_CMP(LCD_BL_DARKCOLOR),
@@ -123,6 +127,29 @@ void lcd_update(void)
 
 void lcd_update_rect(int x_start, int y_start, int width, int height)
 {
+/*
+    Uint32 now = SDL_GetTicks();
+    ++framecount;
+
+    if (now - fps_starttime > 1000)
+    {
+       fps = framecount / (now-fps_starttime) * 1000
+
+       printf("TOMJ: FPS: ~%d\n", fps);
+
+       framecount = 0;
+       fps_starttime = now;
+    }
+*/
+    // Don't update display if not shown
+    if (!n900_display_on)
+        return;
+
+    // Don't update if we don't have the input focus
+    // TODO: Just slow down the update to once a second
+    if (!n900_has_input_focus)
+        return;
+
     sdl_update_rect(lcd_surface, x_start, y_start, width, height,
                     LCD_WIDTH, LCD_HEIGHT, get_lcd_pixel);
     sdl_gui_update(lcd_surface, x_start, y_start, width,
diff --git a/firmware/target/hosted/sdl/lcd-sdl.c b/firmware/target/hosted/sdl/lcd-sdl.c
index 96b1a04..19db68d 100644
--- a/firmware/target/hosted/sdl/lcd-sdl.c
+++ b/firmware/target/hosted/sdl/lcd-sdl.c
@@ -32,8 +32,36 @@ void sdl_update_rect(SDL_Surface *surface, int x_start, int y_start, int width,
 {
     int x, y;
     int xmax, ymax;
-    SDL_Rect dest;
-
+    SDL_Rect src, dest;
+
+#if LCD_DEPTH >= 8 && LCD_PIXELFORMAT & RGB565 \
+    && !(LCD_PIXELFORMAT & RGB565SWAPPED) \
+    && !defined(LCD_STRIDEFORMAT)
+    /* Update complete screen via one blit operation */
+    SDL_Surface *lcd = SDL_CreateRGBSurfaceFrom(lcd_framebuffer, LCD_FBWIDTH, LCD_FBHEIGHT,
+                                                LCD_DEPTH, LCD_FBWIDTH * (LCD_DEPTH/8),
+                                                0, 0, 0, 0);
+
+    src.x = x_start;
+    src.y = y_start;
+    src.w = width;
+    src.h = height;
+
+    dest.x = x_start * display_zoom;
+    dest.y = y_start * display_zoom;
+    dest.w = width * display_zoom;
+    dest.h = height * display_zoom;
+
+    if (display_zoom == 1) {
+        SDL_BlitSurface(lcd, &src, surface, &dest);
+    } else {
+        /* Note: SDL_SoftStretch is currently marked as DO NOT USE
+           but there are no real alternatives for efficent zooming. */
+        SDL_SoftStretch(lcd, &src, surface, &dest);
+    }
+    SDL_FreeSurface(lcd);
+#else
+    /* Very slow pixel-by-pixel drawing */
     ymax = y_start + height;
     xmax = x_start + width;
 
@@ -42,8 +70,6 @@ void sdl_update_rect(SDL_Surface *surface, int x_start, int y_start, int width,
     if(ymax >= max_y)
         ymax = max_y;
 
-    SDL_LockSurface(surface);
-
     dest.w = display_zoom;
     dest.h = display_zoom;
 
@@ -69,8 +95,7 @@ void sdl_update_rect(SDL_Surface *surface, int x_start, int y_start, int width,
         }
 #endif
     }
-
-    SDL_UnlockSurface(surface);
+#endif
 }
 
 void sdl_gui_update(SDL_Surface *surface, int x_start, int y_start, int width,
diff --git a/firmware/target/hosted/sdl/n900-osso.c b/firmware/target/hosted/sdl/n900-osso.c
new file mode 100644
index 0000000..c23fc5e
--- /dev/null
+++ b/firmware/target/hosted/sdl/n900-osso.c
@@ -0,0 +1,168 @@
+#include <libhal.h>
+#include <libosso.h>
+#include <stdio.h>
+
+#include "config.h"
+#include "system.h"
+#include "kernel.h"
+#include "thread.h"
+#include "power.h"
+
+// Battery status information
+#define BME_UDI "/org/freedesktop/Hal/devices/bme"
+#define BATTERY_PERCENTAGE "battery.charge_level.percentage"
+#define BATTER_REMAINING_TIME "battery.remaining_time"
+
+GMainLoop *osso_loop = NULL;
+osso_context_t *osso_ctx = NULL;
+
+/* Volatile is not thread safe (no memory barriers)
+   It just make sure the compiler doesn't cache it 
+   inside a register. This is good enough for us 
+   as it's ok to loose a frame or two when the display is switched on/off. */
+volatile int n900_display_on = 1;
+volatile int n900_has_input_focus = 1;
+volatile int n900_battery_level = 0;
+volatile int n900_remaining_time_sec = 0;
+
+extern void send_battery_level_event(void);
+extern int last_sent_battery_level;
+extern int battery_percent;
+
+void display_status_callback(osso_display_state_t state, gpointer data)
+{
+    if (state == OSSO_DISPLAY_OFF)
+       n900_display_on = 0;
+    else
+       n900_display_on = 1;
+}
+
+
+void get_battery_values(LibHalContext *ctx)
+{
+    // Get initial battery percentage and remaining time
+    n900_battery_level = libhal_device_get_property_int(
+                                                        ctx, BME_UDI,
+                                                        BATTERY_PERCENTAGE, NULL);
+
+    n900_remaining_time_sec = libhal_device_get_property_int(
+                                                        ctx, BME_UDI,
+                                                        BATTER_REMAINING_TIME, NULL);
+
+    printf("[BATTERY] Battery percentage: %d, remaining_time_sec: %d\n", n900_battery_level, n900_remaining_time_sec);
+}
+
+static void on_battery_changed (LibHalContext *ctx,
+                                  const char *udi, 
+                                  const char *key,
+                                  dbus_bool_t is_removed,
+                                  dbus_bool_t is_added)
+{
+    if (!g_str_equal (udi, BME_UDI))
+        return;
+
+    if (!g_str_equal (key, BATTERY_PERCENTAGE) && !g_str_equal (key, BATTER_REMAINING_TIME))
+        return;
+
+    get_battery_values(ctx);
+}
+
+
+int n900_osso_thread (void *unused)
+{
+    GMainContext *ctx = g_main_context_new();
+
+    osso_loop = g_main_loop_new (ctx, FALSE);
+
+    // Register display callback
+    osso_ctx = osso_initialize ("rockbox", "666", FALSE, ctx);
+    osso_hw_set_display_event_cb(osso_ctx, display_status_callback, NULL);
+
+    // Register battery status callback
+    LibHalContext *hal_ctx;
+    hal_ctx = libhal_ctx_new();
+
+    DBusConnection *system_bus = (DBusConnection*)osso_get_sys_dbus_connection(osso_ctx);
+    libhal_ctx_set_dbus_connection(hal_ctx, system_bus);
+
+    libhal_ctx_init(hal_ctx, NULL);
+    libhal_ctx_set_device_property_modified (hal_ctx, on_battery_changed);
+    libhal_device_add_property_watch (hal_ctx, BME_UDI, NULL);
+
+    get_battery_values(hal_ctx);
+
+    printf("TOMJ: Osso thread up and running\n");
+    g_main_loop_run (osso_loop);
+    printf("TOMJ: Osso main loop done\n");
+
+    // Cleanup
+    printf("TOMJ: Before osso_deinit\n"),
+    osso_deinitialize (osso_ctx);
+    printf("TOMJ: Before hal deinit\n");
+    libhal_device_remove_property_watch (hal_ctx, BME_UDI, NULL);
+    libhal_ctx_shutdown (hal_ctx, NULL);
+    libhal_ctx_free(hal_ctx);
+
+    printf("TOMJ: Before looup_unref\n");
+    g_main_loop_unref (osso_loop);
+    g_main_context_unref (ctx);
+
+    printf("TOMJ: Thread should exit\n");
+    return 0;
+}
+
+/// Rockbox battery related functions
+void battery_status_update(void)
+{
+    battery_percent = n900_battery_level;
+    send_battery_level_event();
+}
+
+/* Returns true if any power input is connected - charging-capable
+ * or not. */
+bool power_input_present(void)
+{
+    return false;
+}
+
+unsigned battery_voltage(void)
+{
+    return 0;
+}
+
+/* Returns battery level in percent */
+int battery_level(void)
+{
+    battery_status_update();
+    return n900_battery_level;
+}
+
+/* Return remaining battery time in minutes */
+int battery_time(void)
+{
+    battery_status_update();
+    return n900_remaining_time_sec / 60;
+}
+
+bool battery_level_safe(void)
+{
+    return battery_level() >= 5;
+}
+
+/// Rockbox stubs
+void set_poweroff_timeout(int timeout)
+{
+    (void)timeout;
+}
+
+void reset_poweroff_timer(void)
+{
+}
+
+void shutdown_hw(void)
+{
+}
+
+void cancel_shutdown(void)
+{
+}
diff --git a/firmware/target/hosted/sdl/n900-osso.h b/firmware/target/hosted/sdl/n900-osso.h
new file mode 100644
index 0000000..4c0b13a
--- /dev/null
+++ b/firmware/target/hosted/sdl/n900-osso.h
@@ -0,0 +1,15 @@
+#ifndef n900_osso_h
+#define n900_osso_h
+
+#include <glib.h>
+#include <libosso.h>
+
+extern osso_context_t *osso_ctx;
+extern GMainLoop *osso_loop;
+
+extern volatile int n900_display_on;
+extern volatile int n900_has_input_focus;
+
+int n900_osso_thread(void *unused);
+
+#endif
diff --git a/firmware/target/hosted/sdl/pcm-sdl.c b/firmware/target/hosted/sdl/pcm-sdl.c
index 7780083..71aa995 100644
--- a/firmware/target/hosted/sdl/pcm-sdl.c
+++ b/firmware/target/hosted/sdl/pcm-sdl.c
@@ -24,12 +24,28 @@
 
 #include <stdlib.h>
 #include <stdbool.h>
-#include <SDL.h>
+#include <time.h>
 #include "config.h"
 #include "debug.h"
 #include "sound.h"
 #include "audiohw.h"
 #include "system.h"
+#include "settings.h"
+
+#include "playback.h"
+#include "kernel.h"
+
+#include <SDL.h>
+#include <gst/gst.h>
+#include <gst/app/gstappsrc.h>
+#include <linux/types.h>
+
+// N900 specific libplayback support
+#include <libplayback/playback.h>
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include "n900-osso.h"
 
 #ifdef HAVE_RECORDING
 #include "audiohw.h"
@@ -49,221 +65,366 @@
 extern bool debug_audio;
 #endif
 
-static int sim_volume = 0;
-
 #if CONFIG_CODEC == SWCODEC
-static int cvt_status = -1;
-
-static Uint8* pcm_data;
-static size_t pcm_data_size;
-static size_t pcm_sample_bytes;
-static size_t pcm_channel_bytes;
-
-static struct pcm_udata
-{
-    Uint8 *stream;
-    Uint32 num_in;
-    Uint32 num_out;
-#ifdef DEBUG
-    FILE  *debug;
-#endif
-} udata;
 
-static SDL_AudioSpec obtained;
-static SDL_AudioCVT cvt;
+#define GST_VOLUME_UNKNOWN 2                        // Two is out of range and thus means unintialized
+gdouble gst_last_volume = GST_VOLUME_UNKNOWN;
+
+// Declarations for libplayblack
+pb_playback_t *playback = NULL;
+void playback_state_req_handler(pb_playback_t *pb,
+                                                      enum pb_state_e req_state,
+                                                      pb_req_t *ext_req,
+                                                      void *data);
+void playback_state_req_callback(pb_playback_t *pb,
+            enum pb_state_e granted_state,
+            const char *reason,
+            pb_req_t *req,
+            void *data);
+bool playback_granted = false;
+
+// Gstreamer related vars
+GstCaps *gst_audio_caps = NULL;
+GstElement *gst_pipeline = NULL;
+GstElement *gst_appsrc = NULL;
+GstElement *gst_pulsesink = NULL;
+GstBus *gst_bus = NULL;
+GMainLoop *pcm_loop = NULL;
+
+static __u8* pcm_data = NULL;
+static size_t pcm_data_size = 0;
 
 void pcm_play_lock(void)
 {
-    SDL_LockAudio();
 }
 
 void pcm_play_unlock(void)
 {
-    SDL_UnlockAudio();
-}
-
-static void pcm_dma_apply_settings_nolock(void)
-{
-    cvt_status = SDL_BuildAudioCVT(&cvt, AUDIO_S16SYS, 2, pcm_sampr,
-                    obtained.format, obtained.channels, obtained.freq);
-
-    if (cvt_status < 0) {
-        cvt.len_ratio = (double)obtained.freq / (double)pcm_sampr;
-    }
 }
 
 void pcm_dma_apply_settings(void)
 {
-    pcm_play_lock();
-    pcm_dma_apply_settings_nolock();
-    pcm_play_unlock();
 }
 
 void pcm_play_dma_start(const void *addr, size_t size)
 {
-    pcm_dma_apply_settings_nolock();
+    printf("[TOMJ %d] called pcm_dma_start: addr: %u, size: %u\n", (unsigned int)time(NULL), addr, size);
 
-    pcm_data = (Uint8 *) addr;
+    pcm_data = (__u8 *) addr;
     pcm_data_size = size;
 
-    SDL_PauseAudio(0);
+    if (playback_granted)
+    {
+        // Start playing now
+        gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_PLAYING);
+    } else
+    {
+        // N900: Request change to playing state
+        pb_playback_req_state   (playback,
+                                                PB_STATE_PLAY, // PLAY
+                                                playback_state_req_callback,
+                                                NULL);
+    }
 }
 
 void pcm_play_dma_stop(void)
 {
-    SDL_PauseAudio(1);
-#ifdef DEBUG
-    if (udata.debug != NULL) {
-        fclose(udata.debug);
-        udata.debug = NULL;
-        DEBUGF("Audio debug file closed\n");
-    }
-#endif
+    printf("[TOMJ %d] called pcm_dma_stop\n", (unsigned int)time(NULL));
+
+    gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_NULL);
 }
 
 void pcm_play_dma_pause(bool pause)
 {
+    printf("[TOMJ %d] called pcm_dma_pause: %d\n", time(NULL), pause);
+
     if (pause)
-        SDL_PauseAudio(1);
+        gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_NULL);
     else
-        SDL_PauseAudio(0);
+        gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_PLAYING);
 }
 
 size_t pcm_get_bytes_waiting(void)
 {
+    // printf("[TOMJ %d] called pcm_get_bytes_waiting: %d\n", time(NULL), pcm_data_size);
     return pcm_data_size;
 }
 
-static void write_to_soundcard(struct pcm_udata *udata)
+static void feed_data(GstElement * appsrc, guint size, void *unused)
 {
-#ifdef DEBUG
-    if (debug_audio && (udata->debug == NULL)) {
-        udata->debug = fopen("audiodebug.raw", "ab");
-        DEBUGF("Audio debug file open\n");
-    }
-#endif
-    if (cvt.needed) {
-        Uint32 rd = udata->num_in;
-        Uint32 wr = (double)rd * cvt.len_ratio;
-
-        if (wr > udata->num_out) {
-            wr = udata->num_out;
-            rd = (double)wr / cvt.len_ratio;
-
-            if (rd > udata->num_in)
-            {
-                rd = udata->num_in;
-                wr = (double)rd * cvt.len_ratio;
-            }
-        }
+    pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size);
+
+    if (pcm_data_size != 0)
+    {
+        GstBuffer *buffer = gst_buffer_new ();
+        GstFlowReturn ret;
+
+        GST_BUFFER_DATA (buffer) = pcm_data;
+        GST_BUFFER_SIZE (buffer) = pcm_data_size;
+
+        // printf("feed gst_buffer %p, pcm_data: %p, len %d\n", buffer, pcm_data, pcm_data_size);
+        g_signal_emit_by_name (gst_appsrc, "push-buffer", buffer, &ret);
+        gst_buffer_unref (buffer);
 
-        if (wr == 0 || rd == 0)
+        if (ret != 0)
+            printf("push-buffer error result: %d\n", ret);
+        else
         {
-            udata->num_out = udata->num_in = 0;
-            return;
+            // We fed everything into the buffer
+            // pcm_data += pcm_data_size;
+            // pcm_data_size = 0;
         }
+    } else
+    {
+        printf("feed_data: No Data.\n");
+    }
+}
 
-        if (cvt_status > 0) {
-            cvt.len = rd * pcm_sample_bytes;
-            cvt.buf = (Uint8 *) malloc(cvt.len * cvt.len_mult);
+const void * pcm_play_dma_get_peak_buffer(int *count)
+{
+    /*
+    printf("[TOMJ %d] called pcm_play_dma_get_peak_buffer: %p - %d\n",
+           time(NULL), pcm_data, pcm_data_size / 4);
+    */
 
-            memcpy(cvt.buf, pcm_data, cvt.len);
+    uintptr_t addr = (uintptr_t)pcm_data;
+    *count = pcm_data_size / 4;
+    return (void *)((addr + 2) & ~3);
+}
 
-            SDL_ConvertAudio(&cvt);
-            SDL_MixAudio(udata->stream, cvt.buf, cvt.len_cvt, sim_volume);
 
-            udata->num_in = cvt.len / pcm_sample_bytes;
-            udata->num_out = cvt.len_cvt / pcm_sample_bytes;
+static gboolean
+gst_bus_message (GstBus * bus, GstMessage * message, void *unused)
+{
+  printf("    [gst] got BUS message %s\n",
+      gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
 
-#ifdef DEBUG
-            if (udata->debug != NULL) {
-               fwrite(cvt.buf, sizeof(Uint8), cvt.len_cvt, udata->debug);
-            }
-#endif
-            free(cvt.buf);
-        }
-        else {
-            /* Convert is bad, so do silence */
-            Uint32 num = wr*obtained.channels;
-            udata->num_in = rd;
-            udata->num_out = wr;
-
-            switch (pcm_channel_bytes)
-            {
-            case 1:
-            {
-                Uint8 *stream = udata->stream;
-                while (num-- > 0)
-                    *stream++ = obtained.silence;
-                break;
-                }
-            case 2:
-            {
-                Uint16 *stream = (Uint16 *)udata->stream;
-                while (num-- > 0)
-                    *stream++ = obtained.silence;
-                break;
-                }
-            }
-#ifdef DEBUG
-            if (udata->debug != NULL) {
-               fwrite(udata->stream, sizeof(Uint8), wr, udata->debug);
-            }
-#endif
-        }
-    } else {
-        udata->num_in = udata->num_out = MIN(udata->num_in, udata->num_out);
-        SDL_MixAudio(udata->stream, pcm_data, 
-                     udata->num_out * pcm_sample_bytes, sim_volume);
-#ifdef DEBUG
-        if (udata->debug != NULL) {
-           fwrite(pcm_data, sizeof(Uint8), udata->num_out * pcm_sample_bytes,
-                  udata->debug);
-        }
-#endif
+  switch (GST_MESSAGE_TYPE (message)) {
+    case GST_MESSAGE_ERROR:
+    {
+      GError *err;
+      gchar *debug;
+      gst_message_parse_error (message, &err, &debug);
+
+      printf("[gst] Received error: Src: %s, msg: %s\n", GST_MESSAGE_SRC_NAME(message), err->message);
+
+      g_error_free (err);
+      g_free (debug);
     }
+
+      g_main_loop_quit (pcm_loop);
+      break;
+    case GST_MESSAGE_EOS:
+      g_main_loop_quit (pcm_loop);
+      break;
+  case GST_MESSAGE_STATE_CHANGED:
+  {
+    GstState old_state, new_state;
+
+    gst_message_parse_state_changed (message, &old_state, &new_state, NULL);
+    printf("[gst] Element %s changed state from %s to %s.\n",
+        GST_MESSAGE_SRC_NAME(message),
+        gst_element_state_get_name (old_state),
+        gst_element_state_get_name (new_state));
+    break;
+    }
+    default:
+      break;
+  }
+  return TRUE;
 }
 
-static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
+void n900_configure_appsrc(void)
 {
-    logf("sdl_audio_callback: len %d, pcm %d\n", len, pcm_data_size);
-    udata->stream = stream;
-
-    /* Write what we have in the PCM buffer */
-    if (pcm_data_size > 0)
-        goto start;
-
-    /* Audio card wants more? Get some more then. */
-    while (len > 0) {
-        pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size);
-    start:
-        if (pcm_data_size != 0) {
-            udata->num_in  = pcm_data_size / pcm_sample_bytes;
-            udata->num_out = len / pcm_sample_bytes;
-
-            write_to_soundcard(udata);
-
-            udata->num_in  *= pcm_sample_bytes;
-            udata->num_out *= pcm_sample_bytes;
-
-            pcm_data      += udata->num_in;
-            pcm_data_size -= udata->num_in;
-            udata->stream += udata->num_out;
-            len           -= udata->num_out;
-        } else {
-            DEBUGF("sdl_audio_callback: No Data.\n");
-            break;
-        }
+    // Block push-buffer until there is enough room
+    g_object_set (G_OBJECT(gst_appsrc), "block", TRUE, NULL);
+
+    g_object_set(G_OBJECT(gst_appsrc), "format", GST_FORMAT_BYTES, NULL);
+
+    gst_audio_caps = gst_caps_new_simple("audio/x-raw-int", "width", G_TYPE_INT, (gint)16, "depth", G_TYPE_INT, (gint)16, "channels" ,G_TYPE_INT, (gint)2,
+                                "signed",G_TYPE_BOOLEAN,1,
+                                "rate",G_TYPE_INT,44100,"endianness",G_TYPE_INT,(gint)1234,NULL);
+
+    g_object_set (G_OBJECT(gst_appsrc), "caps", gst_audio_caps, NULL);
+
+    gst_app_src_set_stream_type(GST_APP_SRC(gst_appsrc),
+        GST_APP_STREAM_TYPE_STREAM);
+
+    /* configure the appsrc, we will push data into the appsrc from the
+    * mainloop. */
+    g_signal_connect (gst_appsrc, "need-data", G_CALLBACK (feed_data), NULL);
+}
+
+// Init libplayback: Grant access rights to
+// play audio while the phone is in silent mode
+void n900_init_libplayback(void)
+{
+    DBusConnection *session_bus_raw = (DBusConnection*)osso_get_dbus_connection(osso_ctx);
+
+    playback = pb_playback_new_2(session_bus_raw,
+                                               PB_CLASS_MEDIA,         // TODO: Just for testing. Use MEDIA here
+                                               PB_FLAG_AUDIO,
+                                               PB_STATE_STOP,
+                                               playback_state_req_handler,
+                                               NULL);
+
+    pb_playback_set_stream(playback, "Playback Stream");
+}
+
+/**
+ * Gets called by the policy framework if an important
+ * event arrives: Incoming calls etc.
+ */
+extern SDL_mutex *m;        // HACK
+extern struct event_queue audio_queue;     // HACK
+
+void n900_tell_rockbox_to_stop_audio(void)
+{
+    // Tell rockbox to stop the audio the hard way:
+    // Acquire main SDL kernel lock and post a STOP event
+    // into the audio queue. If we call audio_stop() directly,
+    // we will get a deadlock!
+    // broken: audio_stop();
+    printf("[TOMJ %d] Waiting for kernel mutex\n", time(NULL));
+    SDL_LockMutex(m);
+    printf("[TOMJ %d] Got mutex\n", time(NULL));
+
+    // Stop audio playback and clear queue immediately
+    queue_post(&audio_queue, Q_AUDIO_STOP, 1);
+
+    SDL_UnlockMutex(m);
+    printf("[TOMJ %d] Left mutex\n", time(NULL));
+
+    osso_system_note_infoprint(osso_ctx, "Stopping rockbox playback", NULL);
+}
+
+void playback_state_req_handler(pb_playback_t *pb,
+                                                      enum pb_state_e req_state,
+                                                      pb_req_t *ext_req,
+                                                      void *data)
+{
+    printf("External state change request: state: %s, data: %p\n",
+            pb_state_to_string(req_state), data);
+
+    if (req_state == PB_STATE_STOP && playback_granted)
+    {
+        printf("Stopping playback, might be an incoming call\n");
+
+        playback_granted = false;
+        n900_tell_rockbox_to_stop_audio();
     }
 }
 
-const void * pcm_play_dma_get_peak_buffer(int *count)
+/**
+ * Callback for our own state change request.
+ */
+void playback_state_req_callback(pb_playback_t *pb, enum pb_state_e granted_state, const char *reason, pb_req_t *req, void *data)
 {
-    uintptr_t addr = (uintptr_t)pcm_data;
-    *count = pcm_data_size / 4;
-    return (void *)((addr + 2) & ~3);
+    printf("State request callback: granted_state: %s, reason: %s\n",
+                pb_state_to_string(granted_state), reason);
+
+    /* We are allowed to play audio */
+    if (granted_state == PB_STATE_PLAY)
+    {
+        printf("Start playing...\n");
+        playback_granted = true;
+        gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_PLAYING);
+    } else
+    {
+        printf("Can't start playing. Throwing away play request\n");
+
+        playback_granted = false;
+        n900_tell_rockbox_to_stop_audio();
+    }
+
+    pb_playback_req_completed(pb, req);
+}
+
+void pcm_play_dma_init(void)
+{
+    printf("[TOMJ %d] called pcm_play_dma_init\n", time(NULL));
+
+    n900_init_libplayback();
+
+    GMainContext *ctx = g_main_loop_get_context(osso_loop);
+    pcm_loop = g_main_loop_new (ctx, true);
+
+    gst_init (NULL, NULL);
+
+    // pcm_loop = g_main_loop_new (NULL, false);
+
+    gst_pipeline = gst_pipeline_new ("rockbox");
+
+    gst_appsrc = gst_element_factory_make ("appsrc", NULL);
+
+    gst_pulsesink = gst_element_factory_make ("pulsesink", NULL);
+
+    // Connect elements
+    gst_bin_add_many (GST_BIN (gst_pipeline),
+                        gst_appsrc, gst_pulsesink, NULL);
+    gst_element_link_many (gst_appsrc, gst_pulsesink, NULL);
+
+    // Connect to gstreamer bus of the pipeline
+    gst_bus = gst_pipeline_get_bus (GST_PIPELINE (gst_pipeline));
+    gst_bus_add_watch (gst_bus, (GstBusFunc) gst_bus_message, NULL);
+
+    n900_configure_appsrc();
 }
 
+void pcm_postinit(void)
+{
+}
+
+void pcm_set_mixer_volume(int volume)
+{
+    printf("TOMJ: pcm_set_mixer_volume: %d\n", volume);
+
+    // gstreamer / playbin2 volume range is from 0.00 to 1.00
+    gdouble gst_vol = (gdouble)(volume - VOLUME_MIN) / (gdouble)VOLUME_RANGE;
+
+   // Overwrite rockbox volume settings if the global systm volume was changed.
+   // But don't prevent initial volume setting and only do it we are playing music.
+  // (the pulsesink reports a volume of 1.00 if it's inactive)
+    GstState current_state = GST_STATE_NULL;
+   if (gst_last_volume != GST_VOLUME_UNKNOWN &&
+       gst_element_get_state(GST_ELEMENT(gst_pipeline), &current_state, NULL,
+                             GST_MSECOND) == GST_STATE_CHANGE_SUCCESS
+      && current_state == GST_STATE_PLAYING)
+   {
+        gdouble current_gst_volume = 0;
+        g_object_get (G_OBJECT(gst_pulsesink), "volume", &current_gst_volume, NULL);
+
+        // Cut of the last digits for the external volume change detection comparison
+        // as the double values get()/set() from "playbin2" are unstable
+        printf("TOMJ: DEBUG: current_volume: %f, last_volume: %f\n",
+                    current_gst_volume, gst_last_volume);
+
+        int compare_current_volume = (int)(current_gst_volume * 1000);
+        int compare_last_volume = (int)(gst_last_volume * 1000);
+
+        // Volume changed from outside over change tolerance?
+        const int change_tolerance = 15;
+        if (compare_current_volume - change_tolerance > compare_last_volume ||
+             compare_current_volume + change_tolerance < compare_last_volume)
+        {
+            // Calculate new rockbox volume
+            int new_rockbox_volume = (int)(current_gst_volume * VOLUME_RANGE + VOLUME_MIN) / 10;
+
+            printf("TOMJ: Volume changed too much on the outside (current_volume: %d, last_volume: %d -> Adapting rockbox: %d\n",
+                   compare_current_volume, compare_last_volume, new_rockbox_volume);
+
+            global_settings.volume = new_rockbox_volume;
+            global_status.last_volume_change = current_tick;
+
+            gst_vol = current_gst_volume;
+        }
+   }
+
+    g_object_set (G_OBJECT(gst_pulsesink), "volume", gst_vol, NULL);
+    gst_last_volume = gst_vol;
+}
+
+
 #ifdef HAVE_RECORDING
 void pcm_rec_lock(void)
 {
@@ -312,68 +473,4 @@ unsigned long spdif_measure_frequency(void)
 
 #endif /* HAVE_RECORDING */
 
-void pcm_play_dma_init(void)
-{
-    if (SDL_InitSubSystem(SDL_INIT_AUDIO))
-    {
-        DEBUGF("Could not initialize SDL audio subsystem!\n");
-        return;
-    }
-
-    SDL_AudioSpec wanted_spec;
-#ifdef DEBUG
-    udata.debug = NULL;
-    if (debug_audio) {
-        udata.debug = fopen("audiodebug.raw", "wb");
-        DEBUGF("Audio debug file open\n");
-    }
-#endif
-    /* Set 16-bit stereo audio at 44Khz */
-    wanted_spec.freq = 44100;
-    wanted_spec.format = AUDIO_S16SYS;
-    wanted_spec.channels = 2;
-    wanted_spec.samples = 2048;
-    wanted_spec.callback =
-        (void (SDLCALL *)(void *userdata,
-            Uint8 *stream, int len))sdl_audio_callback;
-    wanted_spec.userdata = &udata;
-
-    /* Open the audio device and start playing sound! */
-    if(SDL_OpenAudio(&wanted_spec, &obtained) < 0) {
-        DEBUGF("Unable to open audio: %s\n", SDL_GetError());
-        return;
-    }
-
-    switch (obtained.format)
-    {
-    case AUDIO_U8:
-    case AUDIO_S8:
-        pcm_channel_bytes = 1;
-        break;
-    case AUDIO_U16LSB:
-    case AUDIO_S16LSB:
-    case AUDIO_U16MSB:
-    case AUDIO_S16MSB:
-        pcm_channel_bytes = 2;
-        break;
-    default:
-        DEBUGF("Unknown sample format obtained: %u\n",
-                (unsigned)obtained.format);
-        return;
-    }
-
-    pcm_sample_bytes = obtained.channels * pcm_channel_bytes;
-
-    pcm_dma_apply_settings_nolock();
-}
-
-void pcm_postinit(void)
-{
-}
-
-void pcm_set_mixer_volume(int volume)
-{
-    sim_volume = volume;
-}
-
 #endif /* CONFIG_CODEC == SWCODEC */
diff --git a/firmware/target/hosted/sdl/system-sdl.c b/firmware/target/hosted/sdl/system-sdl.c
index 6937c37..68f7cd2 100644
--- a/firmware/target/hosted/sdl/system-sdl.c
+++ b/firmware/target/hosted/sdl/system-sdl.c
@@ -19,6 +19,7 @@
  *
  ****************************************************************************/
 
+#include <glib.h>
 #include <SDL.h>
 #include <SDL_thread.h>
 #include <stdlib.h>
@@ -28,6 +29,7 @@
 #include "thread-sdl.h"
 #include "system-sdl.h"
 #include "sim-ui-defines.h"
+#include "n900-osso.h"
 #include "lcd-sdl.h"
 #ifdef HAVE_LCD_BITMAP
 #include "lcd-bitmap.h"
@@ -67,6 +69,7 @@ int wps_verbose_level = 3;
 
 void sys_poweroff(void)
 {
+    printf("TOMJ: Called sys_poweroff\n");
 }
 
 /*
@@ -84,6 +87,7 @@ static int sdl_event_thread(void * param)
 
     SDL_Surface *picture_surface = NULL;
     int width, height;
+    int depth;
 
     /* Try and load the background image. If it fails go without */
     if (background) {
@@ -115,12 +119,18 @@ static int sdl_event_thread(void * param)
             height = SIM_LCD_HEIGHT;
         }
     }
-   
-    
-    if ((gui_surface = SDL_SetVideoMode(width * display_zoom, height * display_zoom, 24, SDL_HWSURFACE|SDL_DOUBLEBUF)) == NULL) {
+
+    depth = LCD_DEPTH;
+    if (depth < 8)
+        depth = 16;
+
+    if ((gui_surface = SDL_SetVideoMode(width * display_zoom, height * display_zoom, depth, SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_FULLSCREEN)) == NULL) {
         panicf("%s", SDL_GetError());
     }
 
+    // Hide mouse cursor
+    SDL_ShowCursor(SDL_DISABLE);
+
     SDL_WM_SetCaption(UI_TITLE, NULL);
 
     if (background && picture_surface != NULL)
@@ -129,10 +139,20 @@ static int sdl_event_thread(void * param)
     /* let system_init proceed */
     SDL_SemPost((SDL_sem *)param);
 
+    // N900: Start osso thread: Listen to display on/off events
+    SDL_Thread *osso_thread = SDL_CreateThread(n900_osso_thread, NULL);
+
     /*
      * finally enter the button loop */
     gui_message_loop();
 
+    /* TODO: We should wait for the n900_osso_thread to actually
+             initialize the main loop. This is currently not thread safe
+             and could be a problem if we get stopped very, very fast -> Use mutex */
+    g_main_loop_quit (osso_loop);
+    printf("TOMJ: Waiting for osso thread\n");
+    SDL_WaitThread(osso_thread, NULL);
+
     if(picture_surface)
         SDL_FreeSurface(picture_surface);
 
@@ -157,6 +177,10 @@ void system_init(void)
 {
     SDL_sem *s;
 
+    // Make glib thread safe
+    g_thread_init(NULL);
+    g_type_init();
+
     if (SDL_Init(SDL_INIT_TIMER))
         panicf("%s", SDL_GetError());
 
diff --git a/firmware/target/hosted/sdl/thread-sdl.c b/firmware/target/hosted/sdl/thread-sdl.c
index 83f1d19..f32aa57 100644
--- a/firmware/target/hosted/sdl/thread-sdl.c
+++ b/firmware/target/hosted/sdl/thread-sdl.c
@@ -59,7 +59,7 @@ static jmp_buf thread_jmpbufs[MAXTHREADS];
 /* this mutex locks out other Rockbox threads while one runs,
  * that enables us to simulate a cooperative environment even if
  * the host is preemptive */
-static SDL_mutex *m;
+SDL_mutex *m;
 #define THREADS_RUN                 0
 #define THREADS_EXIT                1
 #define THREADS_EXIT_COMMAND_DONE   2
diff --git a/tools/configure b/tools/configure
index 1b57210..668b936 100755
--- a/tools/configure
+++ b/tools/configure
@@ -284,7 +284,25 @@ simcc () {
         LDOPTS="$LDOPTS `$sdl --libs`"
     fi
  fi
- 
+
+ # N900 libosso support
+ GCCOPTS="$GCCOPTS `pkg-config --cflags libosso glib-2.0 gthread-2.0`"
+ LDOPTS="$LDOPTS `pkg-config --libs libosso glib-2.0 gthread-2.0`"
+
+ # N900 gstreamer support
+ GCCOPTS="$GCCOPTS `pkg-config --cflags gstreamer-base-0.10 gstreamer-plugins-base-0.10 gstreamer-app-0.10`"
+ LDOPTS="$LDOPTS `pkg-config --libs gstreamer-base-0.10 gstreamer-plugins-base-0.10 gstreamer-app-0.10`"
+
+ # N900 libplayback support
+ GCCOPTS="$GCCOPTS `pkg-config --cflags libplayback-1`"
+ LDOPTS="$LDOPTS `pkg-config --libs libplayback-1`"
+
+ # N900 libhal support
+ GCCOPTS="$GCCOPTS `pkg-config --cflags hal`"
+ LDOPTS="$LDOPTS `pkg-config --libs hal`"
+
+ # N900: Enable arm NEON support
+ GCCOPTS="$GCCOPTS -mfloat-abi=softfp -mfpu=neon"
 
  GCCOPTS="$GCCOPTS -I\$(SIMDIR)"
 
diff --git a/uisimulator/common/SOURCES b/uisimulator/common/SOURCES
index fd256c5..2374103 100644
--- a/uisimulator/common/SOURCES
+++ b/uisimulator/common/SOURCES
@@ -12,5 +12,5 @@ backlight-sim.c
 io.c
 sim_tasks.c
 /* this is still needed for application since it has some stubs */
-powermgmt-sim.c
+/* DISABLED BY TOMJ powermgmt-sim.c */
 stubs.c

Reply via email to