Initial SVN import
[maemo-recorder] / src / maemo-recorder-au.c
diff --git a/src/maemo-recorder-au.c b/src/maemo-recorder-au.c
new file mode 100644 (file)
index 0000000..a58915d
--- /dev/null
@@ -0,0 +1,259 @@
+/* vim: set ts=4 sw=4 et: */
+/*
+ * maemo-recorder-au.c
+ * Support for reading and writing AU/SND files
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <libgnomevfs/gnome-vfs.h>
+#include <stdio.h>
+
+#include "maemo-recorder.h"
+#include "maemo-recorder-au.h"
+
+#define AU_DEFAULT_ANNOTATION_LEN 8
+#define COPY_BUFSIZE 1024
+
+static gint 
+au_write_header(GnomeVFSHandle *handle, guint32 encoding, guint32 rate, guint32 channels, size_t len);
+
+gint 
+au_write(GnomeVFSHandle *handle, guint32 encoding, guint32 rate, guint32 channels, gconstpointer data, size_t len)
+{
+    GnomeVFSFileSize written;
+    GnomeVFSResult res;
+
+    g_assert(handle);
+
+    written = au_write_header(handle, encoding, rate, channels, len);
+    if (written <= 0)
+        return -1;
+
+    res = gnome_vfs_write(handle,
+            data,
+            (GnomeVFSFileSize) len,
+            &written);
+    if (res != GNOME_VFS_OK)
+    {
+        ULOG_ERR("%s: write failed: %s", G_STRFUNC, gnome_vfs_result_to_string(res));
+        return -1;
+    }
+
+    return written;
+}
+
+gint 
+au_write_copy(GnomeVFSHandle *to_handle, guint32 encoding, guint32 rate, guint32 channels, GnomeVFSHandle *from_handle, size_t len)
+{
+    GnomeVFSFileSize written;
+
+    g_assert(to_handle);
+    g_assert(from_handle);
+
+    written = au_write_header(to_handle, encoding, rate, channels, len);
+    if (written <= 0)
+        return -1;
+
+    /* copy the raw data from src to dst */
+    return au_copy_data(to_handle, from_handle, 0);
+}
+
+static gint 
+au_write_header(GnomeVFSHandle *handle, guint32 encoding, guint32 rate, guint32 channels, size_t len)
+{
+    GnomeVFSFileSize written;
+    GnomeVFSResult res;
+
+    struct au_header *hdr = NULL;
+
+    hdr = (struct au_header *) g_malloc0(sizeof(struct au_header) + AU_DEFAULT_ANNOTATION_LEN);
+    hdr->magic = GUINT32_TO_BE(AU_MAGIC);
+    /* http://www.groupground.org/public/external/auformat.html says:
+     * length of annotation field must be non-zero and multiple of 8 bytes, 
+     * the annotation must be terminated with at least one null byte */
+    hdr->data_offset = GUINT32_TO_BE(sizeof(struct au_header) + AU_DEFAULT_ANNOTATION_LEN);
+    hdr->data_size = GUINT32_TO_BE((guint32) len);
+    hdr->encoding = GUINT32_TO_BE(encoding);
+    hdr->sample_rate = GUINT32_TO_BE(rate);
+    hdr->channels = GUINT32_TO_BE(channels);
+
+    res = gnome_vfs_write(handle,
+            (gconstpointer) hdr,
+            (GnomeVFSFileSize) (sizeof(struct au_header) + AU_DEFAULT_ANNOTATION_LEN),
+            &written);
+    if (res != GNOME_VFS_OK)
+    {
+        ULOG_ERR("%s: write failed: %s", G_STRFUNC, gnome_vfs_result_to_string(res));
+        return -1;
+    }
+
+    /*return fwrite(hdr, sizeof(hdr), 1, file);*/
+    return written;
+}
+
+gint
+au_get_info(GnomeVFSHandle *handle, guint32 *format, guint32 *rate, guint32 *channels, guint32 *data_size, guint32 *data_offset)
+{
+    GnomeVFSFileSize read, read_tot = 0;
+    gchar *buf;
+    GnomeVFSResult res;
+    guint32 tmp;
+
+    struct au_header *hdr = NULL;
+
+    hdr = (struct au_header *) g_malloc0(sizeof(struct au_header));
+    buf = (gchar *) hdr;
+
+    /* read in the header */
+    while (read_tot < sizeof(hdr))
+    {
+        res = gnome_vfs_read(handle, 
+                buf,
+                (sizeof(struct au_header) - read_tot),
+                &read);
+        if (res != GNOME_VFS_OK)
+            return -1;
+
+        buf += read;
+        read_tot += read;
+    }
+
+    /* parse it */
+    tmp = GUINT32_FROM_BE(hdr->magic);
+    if (tmp != AU_MAGIC)
+    {
+        ULOG_ERR("%s: wrong magic: %x, expected %x", G_STRFUNC, tmp, AU_MAGIC);
+        return -1;
+    }
+
+    *data_offset = GUINT32_FROM_BE(hdr->data_offset);
+    *data_size = GUINT32_FROM_BE(hdr->data_size);
+    
+    tmp = GUINT32_FROM_BE(hdr->encoding);
+    switch (tmp)
+    {
+        case AU_ENCODING_MULAW_8:
+            *format = FORMAT_PCMU;
+            break;
+            
+        case AU_ENCODING_ALAW_8:
+            *format = FORMAT_PCMA;
+            break;
+
+        case AU_ENCODING_LINEAR_16:
+            *format = FORMAT_PCM;
+            break;
+
+        default:
+            ULOG_ERR("%s: unsupported AU encoding %u", G_STRFUNC, tmp);
+            return -1;
+    }
+
+    *rate = GUINT32_FROM_BE(hdr->sample_rate);
+    *channels = GUINT32_FROM_BE(hdr->channels);
+
+    return read_tot;
+}
+
+gint 
+au_copy_data(GnomeVFSHandle *to_handle, GnomeVFSHandle *from_handle, guint32 from_offset)
+{
+    GnomeVFSResult res;
+    gpointer buffer;
+    gboolean ok = TRUE;
+    GnomeVFSFileSize read, written, written_tot = 0;
+
+    res = gnome_vfs_seek(from_handle,
+                         GNOME_VFS_SEEK_START,
+                         (GnomeVFSFileOffset) from_offset);
+
+    if (res != GNOME_VFS_OK)
+        return -1;
+
+    buffer = (gpointer) g_malloc0(COPY_BUFSIZE);
+
+    while (ok)
+    {
+        read = 0;
+        res = gnome_vfs_read(from_handle, 
+                buffer,
+                COPY_BUFSIZE,
+                &read);
+
+        if (res != GNOME_VFS_OK)
+        {
+            ok = FALSE;
+            if (res != GNOME_VFS_ERROR_EOF)
+            {
+                ULOG_ERR("%s: read failed: %s", G_STRFUNC, gnome_vfs_result_to_string(res));
+                goto error;
+            }
+        }
+
+        if (read > 0)
+        {
+            res = gnome_vfs_write(to_handle,
+                    buffer,
+                    read,
+                    &written);
+
+            if (res != GNOME_VFS_OK)
+            {
+                ULOG_ERR("%s: write failed: %s", G_STRFUNC, gnome_vfs_result_to_string(res));
+                goto error;
+            }
+            if (read != written)
+            {
+                ULOG_ERR("%s: read != written", G_STRFUNC);
+                goto error;
+
+            }
+            written_tot += written;
+        }
+    }
+
+    g_free(buffer);
+    return written_tot;
+
+error:
+    g_free(buffer);
+    return -1;
+
+}
+
+guint32 au_get_encoding(AudioFormat format)
+{
+    switch (format)
+    {
+        case FORMAT_PCMU:
+            return AU_ENCODING_MULAW_8;
+            
+        case FORMAT_PCMA:
+            return AU_ENCODING_ALAW_8;
+
+        case FORMAT_PCM:
+            return AU_ENCODING_LINEAR_16;
+
+        default:
+            ULOG_ERR("%s: unsupported format for AU encoding %u", G_STRFUNC, format);
+            
+            return 0;
+    }
+    return 0;
+}