[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[tyndur-devel] [PATCH 5/5] Sound: Nichtmixer (Nixer)



From: Max Reitz <max@xxxxxxxxxx>

+ Nixer implementiert, der von den Anwendungen aber wie ein normaler
  Mixer angesprochen wird (das Interface wird sich also nicht verändern,
  wenn der Nixer zum Mixer erweitert wird). Mit ihm werden die von den
  Programmen ausgegebenen PCM-Daten so umgewandelt, wie sie der
  Soundkartentreiber benötigt (Anpassung der Samplefrequenz fehlt
  bislang).

Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
 src/modules/c/mixer/Makefile.all  |    7 +
 src/modules/c/mixer/Makefile.conf |    1 +
 src/modules/c/mixer/adriver.c     |  163 +++++++++++++++++++++
 src/modules/c/mixer/input.c       |  292 +++++++++++++++++++++++++++++++++++++
 src/modules/c/mixer/input_queue.c |   91 ++++++++++++
 src/modules/c/mixer/main.c        |  116 +++++++++++++++
 src/modules/c/mixer/manage.c      |  184 +++++++++++++++++++++++
 src/modules/c/mixer/nixer.h       |  154 +++++++++++++++++++
 8 files changed, 1008 insertions(+), 0 deletions(-)
 create mode 100644 src/modules/c/mixer/Makefile.all
 create mode 100644 src/modules/c/mixer/Makefile.conf
 create mode 100644 src/modules/c/mixer/adriver.c
 create mode 100644 src/modules/c/mixer/input.c
 create mode 100644 src/modules/c/mixer/input_queue.c
 create mode 100644 src/modules/c/mixer/main.c
 create mode 100644 src/modules/c/mixer/manage.c
 create mode 100644 src/modules/c/mixer/nixer.h

diff --git a/src/modules/c/mixer/Makefile.all b/src/modules/c/mixer/Makefile.all
new file mode 100644
index 0000000..9075255
--- /dev/null
+++ b/src/modules/c/mixer/Makefile.all
@@ -0,0 +1,7 @@
+shopt -s extglob
+source $LOST_BUILDMK_ROOT/config.sh
+
+echo "LD   $1/apps/mixer"
+$LOST_TOOLS_LD -omixer $LDSCRIPT *.o --start-group $2 --end-group
+
+mv mixer $1/apps/
diff --git a/src/modules/c/mixer/Makefile.conf b/src/modules/c/mixer/Makefile.conf
new file mode 100644
index 0000000..db49762
--- /dev/null
+++ b/src/modules/c/mixer/Makefile.conf
@@ -0,0 +1 @@
+CC_FLAGS_APPEND="-I../../cdi/include"
diff --git a/src/modules/c/mixer/adriver.c b/src/modules/c/mixer/adriver.c
new file mode 100644
index 0000000..7d36aae
--- /dev/null
+++ b/src/modules/c/mixer/adriver.c
@@ -0,0 +1,163 @@
+/******************************************************************************
+ * Copyright (c) 2010 Max Reitz                                               *
+ *                                                                            *
+ * Permission  is hereby granted,  free of charge,  to any person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction,  including without limitation *
+ * the rights to use, copy,  modify, merge, publish,  distribute, sublicense, *
+ * and/or  sell copies of  the Software,  and to permit  persons to  whom the *
+ * Software is furnished to do so, subject to the following conditions:       *
+ *                                                                            *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software.                        *
+ *                                                                            *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED,  INCLUDING BUT NOT LIMITED TO  THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS  FOR A PARTICULAR  PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+ * FROM,  OUT OF OR  IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+ * DEALINGS IN THE SOFTWARE.                                                  *
+ ******************************************************************************/
+
+#include <init.h>
+#include <rpc.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "nixer.h"
+
+// struct sim_adriver.device_count
+static size_t sim_adriver_device_count(struct sim_adriver* drv)
+{
+    int dummy = 0;
+    return rpc_get_int(drv->pid, "NUMODEV", sizeof(dummy), (char*) &dummy);
+}
+
+// struct sim_adriver.is_playback
+static bool sim_adriver_is_playback(struct sim_adriver* drv, int index)
+{
+    return rpc_get_int(drv->pid, "ISPLYBK", sizeof(index), (char*) &index) == 1
+        ? true : false;
+}
+
+// struct sim_adriver.get_stream_information
+static bool sim_adriver_get_stream_information(struct sim_adriver* drv,
+    int card)
+{
+    int strnum[2] = { card, 0 };
+
+    response_t* resp = rpc_get_response(drv->pid, "STRINFO", sizeof(strnum),
+        (char*) &strnum);
+
+    if (resp->data_length != sizeof(*drv->str0)) {
+        free(resp->data);
+        free(resp);
+        return false;
+    }
+
+    free(drv->str0);
+    drv->str0 = resp->data;
+    free(resp);
+
+    return true;
+}
+
+// struct sim_adriver.transfer_data
+static bool sim_adriver_transfer_data(struct sim_adriver* drv, int card,
+    int stream, int buffer, size_t offset, uint32_t shm, size_t shm_size)
+{
+    struct tfrinfo tfrinfo = {
+        .card = card,
+        .stream = stream,
+        .buffer = buffer,
+        .offset = offset,
+        .shm_size = shm_size,
+        .shm = shm
+    };
+
+    return rpc_get_int(drv->pid, "TFRDATA", sizeof(tfrinfo), (char*) &tfrinfo) ?
+        true : false;
+}
+
+// struct sim_adriver.change_device_status
+static cdi_audio_status_t sim_adriver_change_device_status(
+    struct sim_adriver* drv, int card, cdi_audio_status_t status)
+{
+    int data[2] = { card, status };
+    return rpc_get_int(drv->pid, "CHGSTAT", sizeof(data), (char*) &data);
+}
+
+// struct sim_adriver.set_sample_rate
+static int sim_adriver_set_sample_rate(struct sim_adriver* drv, int card,
+    int stream, int rate)
+{
+    int ssr[3] = { card, stream, rate };
+
+    return rpc_get_int(drv->pid, "SSPRATE", sizeof(ssr), (char*) &ssr);
+}
+
+// struct sim_adriver.set_number_of_channels
+static int sim_adriver_set_number_of_channels(struct sim_adriver* drv, int card,
+    int channels)
+{
+    int snoc[2] = { card, channels };
+
+    return rpc_get_int(drv->pid, "SETCHAN", sizeof(snoc), (char*) &snoc);
+}
+
+// struct sim_adriver.get_current_frame
+static int sim_adriver_get_current_frame(struct sim_adriver* drv, int card,
+    int stream)
+{
+    int gcf[2] = { card, stream };
+
+    return rpc_get_int(drv->pid, "GTFRAME", sizeof(gcf), (char*) &gcf);
+}
+
+// struct sim_adriver.disconnect
+static void sim_adriver_disconnect(struct sim_adriver* drv)
+{
+    int tmp = 0;
+    rpc_get_int(drv->pid, "REG", sizeof(tmp), (char*) &tmp);
+    free(drv);
+}
+
+/**
+ * Sucht den Soundtreiber mit dem angegebenen Namen, wird er gefunden, dann
+ * meldet sich die Funktion beim Treiber an und gibt im Erfolgsfall eine
+ * Struktur zurück, die den Treiber repräsentiert.
+ */
+struct sim_adriver* find_driver(const char* name)
+{
+    pid_t pid = init_service_get((char*) name);
+    if (!pid) {
+        fprintf(stderr, "No such service \"%s\".\n", name);
+        return NULL;
+    }
+
+    int tmp = 1;
+    if (rpc_get_int(pid, "REG", sizeof(tmp), (char*) &tmp) != 1) {
+        fprintf(stderr, "Could not register at \"%s\".\n", name);
+        return NULL;
+    }
+
+    struct sim_adriver* ret = calloc(1, sizeof(*ret));
+
+    // Eintragen der PID und der Funktionszeiger
+    ret->pid = pid;
+    ret->device_count = &sim_adriver_device_count;
+    ret->is_playback = &sim_adriver_is_playback;
+    ret->get_stream_information = &sim_adriver_get_stream_information;
+    ret->transfer_data = &sim_adriver_transfer_data;
+    ret->change_device_status = &sim_adriver_change_device_status;
+    ret->set_sample_rate = &sim_adriver_set_sample_rate;
+    ret->set_number_of_channels = &sim_adriver_set_number_of_channels;
+    ret->get_current_frame = &sim_adriver_get_current_frame;
+    ret->disconnect = &sim_adriver_disconnect;
+
+    return ret;
+}
diff --git a/src/modules/c/mixer/input.c b/src/modules/c/mixer/input.c
new file mode 100644
index 0000000..2a0d800
--- /dev/null
+++ b/src/modules/c/mixer/input.c
@@ -0,0 +1,292 @@
+/******************************************************************************
+ * Copyright (c) 2010 Max Reitz                                               *
+ *                                                                            *
+ * Permission  is hereby granted,  free of charge,  to any person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction,  including without limitation *
+ * the rights to use, copy,  modify, merge, publish,  distribute, sublicense, *
+ * and/or  sell copies of  the Software,  and to permit  persons to  whom the *
+ * Software is furnished to do so, subject to the following conditions:       *
+ *                                                                            *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software.                        *
+ *                                                                            *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED,  INCLUDING BUT NOT LIMITED TO  THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS  FOR A PARTICULAR  PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+ * FROM,  OUT OF OR  IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+ * DEALINGS IN THE SOFTWARE.                                                  *
+ ******************************************************************************/
+
+#include <rpc.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <syscall.h>
+
+#include "nixer.h"
+
+extern struct sim_adriver* adriver;
+extern struct input_queue* iq_start;
+
+extern int pbi;
+
+extern bool playing;
+extern int buffers_queued;
+
+extern size_t shm_size;
+
+// Anzahl der Ausgabechannels
+static int out_channel_count;
+
+// PID der aktuellen Quellanwendung
+static pid_t current_source;
+// Daten zum SHM in Richtung Anwendung
+static uint32_t current_input_shm_id;
+static size_t current_input_shm_size;
+static void* current_input_shm;
+
+// Anzahl der Eingabechannels
+static int input_channel_count;
+// Eingabeformat
+static cdi_audio_sample_format_t input_format;
+
+/**
+ * Wird von der Anwendung einerseits aufgerufen, um den Mixer zu konfigurieren
+ * und andererseits, um sich bei ihm anzumelden -- da dieser Mixer ein Nixer
+ * ist, kann immer nur eine Anwendung zu einem bestimmten Zeitpunkt bedient
+ * werden.
+ */
+RPC(mixer_config)
+{
+    struct mixer_config* mc = data;
+
+    if (length != sizeof(*mc)) {
+        rpc_int_resp(0);
+        return;
+    }
+
+    // Wenn schon eine Anwendung angemeldet ist, kann die Anfrage nicht
+    // akzeptiert werden
+    if (current_source && (src != current_source)) {
+        rpc_int_resp(0);
+        return;
+    }
+
+    // Ist die Anzahl der Channels 0, dann meldet sich die Anwendung ab
+    if (current_source && !mc->channel_count) {
+        current_source = 0;
+        current_input_shm_size = 0;
+        current_input_shm = NULL;
+        close_shared_memory(current_input_shm_id);
+
+        trash_sound(false);
+
+        rpc_int_resp(1);
+        return;
+    }
+
+    // Aktuelle Anwendung setzen
+    current_source = src;
+
+    // Aktuelles Eingabeformat speichern
+    input_format = mc->sample_format;
+    input_channel_count = mc->channel_count;
+
+    // TODO: Rückgabewert nutzen und bei Bedarf resamplen
+    adriver->set_sample_rate(adriver, pbi, 0, mc->sample_rate);
+
+    // Versuchen, die Anzahl der Ausgabechannel der Anzahl der Eingabechannel
+    // anzupassen
+    out_channel_count =
+        adriver->set_number_of_channels(adriver, pbi, mc->channel_count);
+
+    rpc_int_resp(1);
+}
+
+/**
+ * Wird von der Anwendung aufgerufen, um einen SHM zur Übertragung von Daten zum
+ * Nixer zu erhalten.
+ */
+RPC(get_shm)
+{
+    if ((length != sizeof(size_t)) || (src != current_source)) {
+        rpc_dword_resp(0);
+        return;
+    }
+
+    // Wenn es bereits einen SHM gibt, muss dieser zuerst geschlossen werden.
+    if (current_input_shm_id) {
+        current_input_shm = NULL;
+        close_shared_memory(current_input_shm_id);
+    }
+
+    current_input_shm_size = *((size_t*) data);
+
+    current_input_shm_id = create_shared_memory(current_input_shm_size);
+    if (current_input_shm_id) {
+        current_input_shm = open_shared_memory(current_input_shm_id);
+    }
+
+    // SHM-ID zurückgeben
+    rpc_dword_resp(current_input_shm_id);
+}
+
+/**
+ * Überträgt die Daten aus dem Eingangs-SHM in die Eingangsqueue und bereitet
+ * sie so auf das Abspielen durch den Soundtreiber vor.
+ */
+RPC(play_input)
+{
+    if ((src != current_source) || (length != sizeof(uint32_t))) {
+        rpc_int_resp(0);
+        return;
+    }
+
+    // Kleine Kontrolle
+    if (*((uint32_t*) data) != current_input_shm_id) {
+        rpc_int_resp(0);
+        return;
+    }
+
+    // Daten über das aktuell erstellte Eingangspaket
+    size_t current_size = 0, remaining = current_input_shm_size;
+    uint8_t* current_buffer;
+    struct input_queue* current_packet;
+
+    uint8_t* input_buffer = current_input_shm;
+
+    size_t input_size = sample_size[input_format];
+    size_t output_size = sample_size[adriver->str0->sample_format];
+
+    // Gibt an, ob ein neues Paket erstellt wurde
+    bool allocated;
+
+    // Nun werden die Daten vom Eingangsformat in das Ausgabeformat umgewandelt
+    while (remaining) {
+        if ((allocated = input_queue_complete())) {
+            // Letztes Paket vollständig, also erstellen wir ein neues
+            current_packet = malloc(sizeof(*current_packet));
+            current_buffer = malloc(shm_size);
+            current_packet->buffer = current_buffer;
+
+            current_size = shm_size;
+        } else {
+            // Letztes Paket unvollständig, also füllen wir das
+            current_packet = last_input_queue_packet();
+            current_size = current_packet->missing;
+
+            current_buffer = current_packet->buffer +
+                shm_size - current_packet->missing;
+        }
+
+        // TODO: Müsste man bei mehr Sampleformaten anpassen (außer Signed
+        // Integer mit Little Endian)
+        int_fast32_t samples[(out_channel_count > input_channel_count) ?
+            out_channel_count : input_channel_count];
+        size_t in_frame_size = input_channel_count * input_size;
+        size_t out_frame_size = out_channel_count * output_size;
+
+        // Solange noch Eingabedaten vorhanden sind und der Ausgabepuffer nicht
+        // voll ist
+        // TODO: Das funktioniert, ist aber langsam. Mit MMX oder so kann man da
+        // bestimmt was machen.
+        while (current_size && remaining) {
+            int_fast32_t val = 0;
+            int i, j;
+
+            // Eingabesamples einlesen
+            for (j = 0; j < input_channel_count; j++) {
+                val = 0;
+                for (i = 0; i < input_size; i++) {
+                    val |= *(input_buffer++) << (i * 8);
+                }
+
+                // Für die Ausgabe zurechtshiften
+                val <<= 8 * (4 - input_size);
+                val >>= 8 * (4 - output_size);
+
+                samples[j] = val;
+            }
+
+            remaining -= in_frame_size;
+
+            // TODO: Das geht sicher besser
+            for (j = input_channel_count; j < out_channel_count; j++) {
+                samples[j] = val;
+            }
+
+            // Ausgabesamples schreiben
+            for (j = 0; j < out_channel_count; j++) {
+                for (i = 0; i < output_size; i++) {
+                    *(current_buffer++) = samples[j] & 0xFF;
+                    samples[j] >>= 8;
+                }
+            }
+
+            current_size -= out_frame_size;
+        }
+
+        current_packet->missing = current_size;
+        if (allocated) {
+            // Wurde das Paket neu erstellt, dann muss es noch an die
+            // Eingangsqueue angehangen werden
+            input_queue_push(current_packet);
+        }
+    }
+
+    // Abspielvorgang starten
+    play();
+
+    rpc_int_resp(1);
+}
+
+/**
+ * Gibt die Anzahl der im Puffer vorhandenen Bytes zurück
+ */
+RPC(get_position)
+{
+    if (!playing && (iq_start == NULL)) {
+        rpc_int_resp(0);
+        return;
+    }
+
+    int queued = 0;
+
+    // FIXME: Warum auch immer, jedenfalls ist es schlecht, wenn diese Pakete
+    // mitgezählt werden und man mehr als einmal laut möchte
+    /*
+    // Berechnet, wie viele Bytes bei der Soundkarte warten
+    if (buffers_queued > 1) {
+        queued = (buffers_queued - 1) * shm_size;
+    }
+    */
+
+    // Untersucht, wie viele Bytes in der Eingangsqueue warten
+    struct input_queue* iq = iq_start;
+
+    while (iq != NULL) {
+        queued += shm_size - iq->missing;
+        iq = iq->next;
+    }
+
+    // Fragt ab, wie viele Bytes im aktuell in der Soundkarte abgespielten
+    // Puffer noch warten
+    if (playing && buffers_queued) {
+        queued += (adriver->str0->buffer_size -
+            adriver->get_current_frame(adriver, pbi, 0) * out_channel_count) *
+            sample_size[adriver->str0->sample_format];
+    }
+
+    // Die Größe des Ausgabe-SHMs abziehen, damit die Anwendung immer genügend
+    // Daten liefert, sodass der gefüllt werden kann.
+    queued -= shm_size;
+    if (queued < 0) {
+        queued = 0;
+    }
+
+    rpc_int_resp(queued);
+}
diff --git a/src/modules/c/mixer/input_queue.c b/src/modules/c/mixer/input_queue.c
new file mode 100644
index 0000000..e69205b
--- /dev/null
+++ b/src/modules/c/mixer/input_queue.c
@@ -0,0 +1,91 @@
+/******************************************************************************
+ * Copyright (c) 2010 Max Reitz                                               *
+ *                                                                            *
+ * Permission  is hereby granted,  free of charge,  to any person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction,  including without limitation *
+ * the rights to use, copy,  modify, merge, publish,  distribute, sublicense, *
+ * and/or  sell copies of  the Software,  and to permit  persons to  whom the *
+ * Software is furnished to do so, subject to the following conditions:       *
+ *                                                                            *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software.                        *
+ *                                                                            *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED,  INCLUDING BUT NOT LIMITED TO  THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS  FOR A PARTICULAR  PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+ * FROM,  OUT OF OR  IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+ * DEALINGS IN THE SOFTWARE.                                                  *
+ ******************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "nixer.h"
+
+// Anfang und Ende der Eingangsqueue
+struct input_queue* iq_start;
+struct input_queue** iq_end = &iq_start;
+
+/**
+ * Fügt ein Paket an das Ende der Eingangsqueue an
+ */
+void input_queue_push(struct input_queue* packet)
+{
+    packet->next = NULL;
+
+    *iq_end = packet;
+    iq_end = &packet->next;
+}
+
+/**
+ * Entfernt ein Paket vom Beginn der Eingangsqueue und gibt es zurück.
+ */
+struct input_queue* input_queue_pop(void)
+{
+    if (iq_start == NULL) {
+        return NULL;
+    }
+
+    struct input_queue* packet = iq_start;
+    iq_start = packet->next;
+
+    // Ende der Queue, somit wird iq_end ungültig und muss auf iq_start zeigen
+    if (packet->next == NULL) {
+        iq_end = &iq_start;
+    }
+
+    return packet;
+}
+
+/**
+ * Gibt einen Zeiger auf das letzte Paket in der Eingangsqueue zurück.
+ */
+struct input_queue* last_input_queue_packet(void)
+{
+    // Keine Pakete in der Queue
+    if (iq_end == &iq_start) {
+        return NULL;
+    }
+
+    // Da iq_end ein Zeiger auf das next-Feld des letzten Pakets ist, kann man
+    // daraus die Adresse dieses Paketes ermitteln.
+    return (void*) ((uintptr_t) iq_end - offsetof(struct input_queue, next));
+}
+
+/**
+ * Ist der letzte Puffer in der Eingangsqeue vollständig gefüllt, so wird true
+ * zurückgegeben, ebenso, wenn die Queue leer ist, sonst false.
+ */
+bool input_queue_complete(void)
+{
+    struct input_queue* last_packet = last_input_queue_packet();
+    if (last_packet == NULL) {
+        return true;
+    }
+
+    return !last_packet->missing;
+}
diff --git a/src/modules/c/mixer/main.c b/src/modules/c/mixer/main.c
new file mode 100644
index 0000000..d900a71
--- /dev/null
+++ b/src/modules/c/mixer/main.c
@@ -0,0 +1,116 @@
+/******************************************************************************
+ * Copyright (c) 2010 Max Reitz                                               *
+ *                                                                            *
+ * Permission  is hereby granted,  free of charge,  to any person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction,  including without limitation *
+ * the rights to use, copy,  modify, merge, publish,  distribute, sublicense, *
+ * and/or  sell copies of  the Software,  and to permit  persons to  whom the *
+ * Software is furnished to do so, subject to the following conditions:       *
+ *                                                                            *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software.                        *
+ *                                                                            *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED,  INCLUDING BUT NOT LIMITED TO  THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS  FOR A PARTICULAR  PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+ * FROM,  OUT OF OR  IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+ * DEALINGS IN THE SOFTWARE.                                                  *
+ ******************************************************************************/
+
+#include <init.h>
+#include <rpc.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include <sys/types.h>
+
+#include "nixer.h"
+
+// Daten zum SHM in Richtung Soundtreiber
+uint32_t shm_id;
+void* shm;
+size_t shm_size;
+
+// Index der Ausgabesoundkarte
+int pbi = -1;
+
+// Verwendeter Soundtreiber
+struct sim_adriver* adriver;
+
+int main(int argc, char* argv[])
+{
+    if (argc != 2) {
+        fprintf(stderr, "Usage: mixer <driver>\n");
+        return 1;
+    }
+
+    if ((adriver = find_driver(argv[1])) == NULL) {
+        fprintf(stderr, "No such service \"%s\".\n", argv[1]);
+        return 1;
+    }
+
+    printf("Found service \"%s\" found and registered there.\n", argv[1]);
+
+    int devnum = adriver->device_count(adriver);
+
+    printf("Devices reported: %i\n", devnum);
+
+    int i;
+
+    for (i = 0; i < devnum; i++) {
+        if (adriver->is_playback(adriver, i)) {
+            pbi = i;
+            break;
+        }
+    }
+
+    if (pbi == -1) {
+        adriver->disconnect(adriver);
+        fprintf(stderr, "No playback devices found.\n");
+        return 1;
+    }
+
+    printf("Device %i is a playback device.\n", pbi);
+
+    if (adriver->get_stream_information(adriver, pbi) == false) {
+        adriver->disconnect(adriver);
+        fprintf(stderr, "Invalid answer.\n");
+        return 1;
+    }
+
+    switch (adriver->str0->sample_format) {
+        case CDI_AUDIO_8SI:
+        case CDI_AUDIO_16SI:
+        case CDI_AUDIO_32SI:
+            shm_size = adriver->str0->buffer_size *
+                sample_size[adriver->str0->sample_format];
+            break;
+        default:
+            fprintf(stderr, "Unknown sample format %i.\n",
+                (int) adriver->str0->sample_format);
+            adriver->disconnect(adriver);
+            return 1;
+    }
+
+    shm_id = create_shared_memory(shm_size);
+    shm = open_shared_memory(shm_id);
+
+    adriver->change_device_status(adriver, pbi, CDI_AUDIO_STOP);
+
+    init_service_register("mixer");
+
+    register_message_handler("BUFCOM", &buffer_completed);
+    register_message_handler("MIXCONF", &mixer_config);
+    register_message_handler("GETSHM", &get_shm);
+    register_message_handler("PLAY", &play_input);
+    register_message_handler("POS", &get_position);
+
+    for (;;) {
+        wait_for_rpc();
+    }
+}
diff --git a/src/modules/c/mixer/manage.c b/src/modules/c/mixer/manage.c
new file mode 100644
index 0000000..16ec93f
--- /dev/null
+++ b/src/modules/c/mixer/manage.c
@@ -0,0 +1,184 @@
+/******************************************************************************
+ * Copyright (c) 2010 Max Reitz                                               *
+ *                                                                            *
+ * Permission  is hereby granted,  free of charge,  to any person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction,  including without limitation *
+ * the rights to use, copy,  modify, merge, publish,  distribute, sublicense, *
+ * and/or  sell copies of  the Software,  and to permit  persons to  whom the *
+ * Software is furnished to do so, subject to the following conditions:       *
+ *                                                                            *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software.                        *
+ *                                                                            *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED,  INCLUDING BUT NOT LIMITED TO  THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS  FOR A PARTICULAR  PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+ * FROM,  OUT OF OR  IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+ * DEALINGS IN THE SOFTWARE.                                                  *
+ ******************************************************************************/
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cdi/audio.h>
+
+#include "nixer.h"
+
+extern struct input_queue* iq_start;
+extern struct input_queue** iq_end;
+extern struct sim_adriver* adriver;
+
+extern int pbi;
+
+extern uint32_t shm_id;
+extern void* shm;
+extern size_t shm_size;
+
+// true, wenn derzeit Daten abgespielt werden
+volatile bool playing;
+
+// Anzahl der bereits zum Soundkartentreiber geschickten Puffer (sollte
+// mindestens zwei sein, damit keine Lücken beim Abspielen entstehen)
+int buffers_queued;
+// Nächster zu sendender Puffer
+int current_buffer;
+
+/**
+ * Überträgt, wenn möglich, so viele Pakete aus der Eingangsqueue an den
+ * Treiber, bis dieser zwei Pakete zu bearbeiten hat (ein Paket wird
+ * aktuell bearbeitet, ein zweites dient als Puffer).
+ */
+void send_buffer(void)
+{
+    struct input_queue* ip;
+
+    // Klasse, Queue leer oder erstes Paket unvollständig...
+    if ((iq_start == NULL) || (iq_start->missing)) {
+        if (!buffers_queued) {
+            adriver->change_device_status(adriver, pbi, CDI_AUDIO_STOP);
+            playing = false;
+        }
+        return;
+    }
+
+    while (buffers_queued < 2) {
+        ip = input_queue_pop();
+
+        if ((ip == NULL) || (ip->missing)) {
+            break;
+        }
+
+        // Die Daten im Paket besitzen bereits das Format, dass der Treiber
+        // erwartet
+        memcpy(shm, ip->buffer, shm_size);
+
+        // Paket freigeben
+        free(ip->buffer);
+        free(ip);
+
+        // Daten an den Treiber übergeben
+        if (!adriver->transfer_data(adriver, pbi, 0, current_buffer, 0, shm_id,
+            shm_size))
+        {
+            fprintf(stderr,
+                "[nixer] Failed to transfer data (this is fatal).\n");
+            adriver->disconnect(adriver);
+            abort();
+        }
+
+        buffers_queued++;
+        current_buffer = (current_buffer + 1) % adriver->str0->num_buffers;
+    }
+}
+
+/**
+ * Startet den Abspielvorgang und überträgt die ersten Pakete an den Treiber.
+ */
+void play(void)
+{
+    bool has_played = playing;
+
+    playing = true;
+
+    send_buffer();
+
+    if (playing && !has_played) {
+        // Wiedergabe im Erfolgsfall starten
+        adriver->change_device_status(adriver, pbi, CDI_AUDIO_PLAY);
+    }
+}
+
+/**
+ * Verwirft alle Eingabedaten und beendet den Abspielvorgang.
+ */
+void trash_sound(bool immediately)
+{
+    if (immediately) {
+        playing = false;
+        adriver->change_device_status(adriver, pbi, CDI_AUDIO_STOP);
+        buffers_queued = 0;
+
+        struct input_queue* ip = iq_start;
+        iq_end = &iq_start;
+        iq_start = NULL;
+
+        while (ip != NULL) {
+            free(ip->buffer);
+
+            void* tmp = ip;
+            ip = ip->next;
+            free(tmp);
+        }
+    }
+
+    while (playing);
+
+    current_buffer = (current_buffer + 1) % adriver->str0->num_buffers;
+}
+
+/**
+ * Wird vom Treiber aufgerufen, wenn ein Puffer abgeschlossen wurde.
+ */
+RPC(buffer_completed)
+{
+    if (length != sizeof(size_t)) {
+        rpc_int_resp(0);
+        return;
+    }
+
+    // current_buffer sollte derzeit auf diesem Wert stehen
+    int expected =
+        (*(size_t*) data + buffers_queued) % adriver->str0->num_buffers;
+
+    buffers_queued--;
+
+    // Merkwürdigerweise wurden in diesem Fall mehr Puffer abgespielt, als
+    // eigentlich an die Karte übergeben wurden.
+    if (buffers_queued < 0) {
+        fprintf(stderr, "[nixer] Strange underrun; %i packets missing.\n",
+            -buffers_queued);
+
+        // Versuch, zu retten, was zu retten ist
+        current_buffer = (*(size_t*) data + 1) % adriver->str0->num_buffers;
+        buffers_queued = 0;
+    }
+
+    // current_buffer entspricht nicht dem Wert, den die Soundkarte erwartet
+    if (expected != current_buffer) {
+        fprintf(stderr, "[nixer] Buffer index discrepancy; %i expected, but "
+            "should be %i\n", current_buffer, expected);
+
+        current_buffer = expected;
+    }
+
+    // Nächsten Puffer aus der Eingabequeue senden
+    send_buffer();
+
+    rpc_int_resp(0);
+}
diff --git a/src/modules/c/mixer/nixer.h b/src/modules/c/mixer/nixer.h
new file mode 100644
index 0000000..291ad14
--- /dev/null
+++ b/src/modules/c/mixer/nixer.h
@@ -0,0 +1,154 @@
+/******************************************************************************
+ * Copyright (c) 2010 Max Reitz                                               *
+ *                                                                            *
+ * Permission  is hereby granted,  free of charge,  to any person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction,  including without limitation *
+ * the rights to use, copy,  modify, merge, publish,  distribute, sublicense, *
+ * and/or  sell copies of  the Software,  and to permit  persons to  whom the *
+ * Software is furnished to do so, subject to the following conditions:       *
+ *                                                                            *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software.                        *
+ *                                                                            *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED,  INCLUDING BUT NOT LIMITED TO  THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS  FOR A PARTICULAR  PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+ * FROM,  OUT OF OR  IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+ * DEALINGS IN THE SOFTWARE.                                                  *
+ ******************************************************************************/
+
+#ifndef NIXER_H
+#define NIXER_H
+
+#include <rpc.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cdi/audio.h>
+
+// Macros, damit man ein paar RPC-Sachen schneller schreiben kann
+#define RPC(name) \
+    void name(pid_t src, uint32_t corr_id, size_t length, void* data)
+
+#define rpc_int_resp(val)   rpc_send_int_response(src, corr_id, val)
+#define rpc_dword_resp(val) rpc_send_dword_response(src, corr_id, val)
+
+// Abstrahiert den aktuellen Ausgabetreiber
+struct sim_adriver {
+    // PID des Treibers
+    pid_t pid;
+
+    // Zu verwendender Stream (dieser Nichtmixer = Nixer unterstützt nur eine
+    // Quellanwendung, daher wäre die Verwendung mehrerer Ausgabestreams nicht
+    // sehr sinnvoll)
+    struct cdi_audio_stream* str0;
+
+    /**
+     * Gibt die Anzahl der vom Treiber verwalteten Geräte zurück.
+     */
+    size_t (*device_count)(struct sim_adriver* drv);
+    /**
+     * Gibt true zurück, wenn die Soundkarte mit dem angegebenen Index ein
+     * Ausgabegerät ist.
+     */
+    bool (*is_playback)(struct sim_adriver* drv, int index);
+    /**
+     * Füllt das str0-Feld mit Informationen über den ersten Stream der
+     * ausgewählten Karte.
+     */
+    bool (*get_stream_information)(struct sim_adriver* drv, int card);
+    /**
+     * Tauscht Daten mit dem Stream der angegebenen Karte aus (stream sollte
+     * normalerweise 0 sein), buffer ist der Index des zu verwendenden Puffers,
+     * offset der Zieloffset darin, shm die SHM-ID zum Datenaustausch und
+     * shm_size die Größe des SHMs.
+     * Bei Erfolg wird true zurückgegeben, sonst false.
+     */
+    bool (*transfer_data)(struct sim_adriver* drv, int card, int stream,
+        int buffer, size_t offset, uint32_t shm, size_t shm_size);
+    /**
+     * Verändert den Zustand einer Soundkarte (z. B. Abspielen oder Anhalten).
+     * Zurückgegeben wird der tatsächlich gesetzte Zustand.
+     */
+    cdi_audio_status_t (*change_device_status)(struct sim_adriver* drv,
+        int card, cdi_audio_status_t status);
+    /**
+     * Versucht, die Samplerate (in Hz) des angegebenen Streams zu verändern.
+     * Dies muss nicht immer gelingen, die tatsächlich eingestellte Samplerate
+     * wird zurückgegeben (z. B. könnte das Setzen der Samplerate 44101 Hz zu
+     * einer tatsächlichen Samplerate von 44100 Hz führen).
+     */
+    int (*set_sample_rate)(struct sim_adriver* drv, int card, int stream,
+        int rate);
+    /**
+     * Versucht, die Anzahl der Ausgabechannel der angegebenen Karte zu setzen.
+     * Zurückgegeben wird die tatsächliche Anzahl von Channeln (Versucht man,
+     * eine Stereokarte mit der Channelanzahl 6 in den 5.1-Modus zu setzen, dann
+     * wird wohl 2 zurückgegeben, weil nur zwei Channel unterstützt werden).
+     */
+    int (*set_number_of_channels)(struct sim_adriver* drv, int card,
+        int channels);
+    /**
+     * Gibt den Index des aktuell abgespielten Frames zurück.
+     */
+    int (*get_current_frame)(struct sim_adriver* drv, int card, int stream);
+    /**
+     * Löst die Verbindung zu einem Audiotreiber und gibt den Speicher der
+     * übergebenen Struktur frei.
+     */
+    void (*disconnect)(struct sim_adriver* drv);
+};
+
+// Struktur, die für transfer_data-RPC verwendet wird.
+struct tfrinfo {
+    int card, stream, buffer;
+    size_t offset, shm_size;
+    uint32_t shm;
+} __attribute__((packed));
+
+// Struktur, die von der Anwendung genutzt wird, um dem Mixer Informationen
+// zum Sampling mitzuteilen
+struct mixer_config {
+    int sample_rate, channel_count;
+    cdi_audio_sample_format_t sample_format;
+} __attribute__((packed));
+
+// Queue mit den eingehenden Daten
+struct input_queue {
+    struct input_queue* next;
+    // Puffer mit den an die Soundkarte zu schickenden Samples
+    void* buffer;
+    // Anzahl der noch fehlenden Bytes, bis der Puffer vollständig gefüllt ist
+    size_t missing;
+} __attribute__((packed));
+
+// Größe eines Samples in Abhängigkeit vom Sampleformat
+static const size_t sample_size[] = {
+    2, // CDI_AUDIO_16SI
+    1, // CDI_AUDIO_8SI
+    4  // CDI_AUDIO_32SI
+};
+
+void input_queue_push(struct input_queue* packet);
+struct input_queue* input_queue_pop(void);
+struct input_queue* last_input_queue_packet(void);
+bool input_queue_complete(void);
+
+struct sim_adriver* find_driver(const char* name);
+
+void send_buffer(void);
+void play(void);
+void trash_sound(bool immediately);
+
+RPC(buffer_completed);
+RPC(get_position);
+RPC(get_shm);
+RPC(mixer_config);
+RPC(play_input);
+
+#endif
-- 
1.7.1