simpler ui
[maemo-recorder] / src / maemo-recorder-au.c
1 /* vim: set sts=4 sw=4 et: */
2 /*
3  * maemo-recorder-au.c
4  * Support for reading and writing AU/SND files
5  *
6  * Copyright (C) 2006 Nokia Corporation
7  *
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16  * for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23
24 #include <libgnomevfs/gnome-vfs.h>
25 #include <stdio.h>
26
27 #include "maemo-recorder.h"
28 #include "maemo-recorder-au.h"
29
30 #define AU_DEFAULT_ANNOTATION_LEN 8
31 #define COPY_BUFSIZE 1024
32
33 static gint 
34 au_write_header(GnomeVFSHandle *handle, guint32 encoding, guint32 rate, guint32 channels, size_t len);
35
36 gint 
37 au_write(GnomeVFSHandle *handle, guint32 encoding, guint32 rate, guint32 channels, gconstpointer data, size_t len)
38 {
39     GnomeVFSFileSize written;
40     GnomeVFSResult res;
41
42     g_assert(handle);
43
44     written = au_write_header(handle, encoding, rate, channels, len);
45     if (written <= 0)
46         return -1;
47
48     res = gnome_vfs_write(handle,
49             data,
50             (GnomeVFSFileSize) len,
51             &written);
52     if (res != GNOME_VFS_OK)
53     {
54         ULOG_ERR("%s: write failed: %s", G_STRFUNC, gnome_vfs_result_to_string(res));
55         return -1;
56     }
57
58     return written;
59 }
60
61 gint 
62 au_write_copy(GnomeVFSHandle *to_handle, guint32 encoding, guint32 rate, guint32 channels, GnomeVFSHandle *from_handle, size_t len)
63 {
64     GnomeVFSFileSize written;
65
66     g_assert(to_handle);
67     g_assert(from_handle);
68
69     written = au_write_header(to_handle, encoding, rate, channels, len);
70     if (written <= 0)
71         return -1;
72
73     /* copy the raw data from src to dst */
74     return au_copy_data(to_handle, from_handle, 0);
75 }
76
77 static gint 
78 au_write_header(GnomeVFSHandle *handle, guint32 encoding, guint32 rate, guint32 channels, size_t len)
79 {
80     GnomeVFSFileSize written;
81     GnomeVFSResult res;
82
83     struct au_header *hdr = NULL;
84
85     hdr = (struct au_header *) g_malloc0(sizeof(struct au_header) + AU_DEFAULT_ANNOTATION_LEN);
86     hdr->magic = GUINT32_TO_BE(AU_MAGIC);
87     /* http://www.groupground.org/public/external/auformat.html says:
88      * length of annotation field must be non-zero and multiple of 8 bytes, 
89      * the annotation must be terminated with at least one null byte */
90     hdr->data_offset = GUINT32_TO_BE(sizeof(struct au_header) + AU_DEFAULT_ANNOTATION_LEN);
91     hdr->data_size = GUINT32_TO_BE((guint32) len);
92     hdr->encoding = GUINT32_TO_BE(encoding);
93     hdr->sample_rate = GUINT32_TO_BE(rate);
94     hdr->channels = GUINT32_TO_BE(channels);
95
96     res = gnome_vfs_write(handle,
97             (gconstpointer) hdr,
98             (GnomeVFSFileSize) (sizeof(struct au_header) + AU_DEFAULT_ANNOTATION_LEN),
99             &written);
100     if (res != GNOME_VFS_OK)
101     {
102         ULOG_ERR("%s: write failed: %s", G_STRFUNC, gnome_vfs_result_to_string(res));
103         return -1;
104     }
105
106     /*return fwrite(hdr, sizeof(hdr), 1, file);*/
107     return written;
108 }
109
110 gint
111 au_get_info(GnomeVFSHandle *handle, guint32 *format, guint32 *rate, guint32 *channels, guint32 *data_size, guint32 *data_offset)
112 {
113     GnomeVFSFileSize read, read_tot = 0;
114     gchar *buf;
115     GnomeVFSResult res;
116     guint32 tmp;
117
118     struct au_header *hdr = NULL;
119
120     hdr = (struct au_header *) g_malloc0(sizeof(struct au_header));
121     buf = (gchar *) hdr;
122
123     /* read in the header */
124     while (read_tot < sizeof(hdr))
125     {
126         res = gnome_vfs_read(handle, 
127                 buf,
128                 (sizeof(struct au_header) - read_tot),
129                 &read);
130         if (res != GNOME_VFS_OK)
131             return -1;
132
133         buf += read;
134         read_tot += read;
135     }
136
137     /* parse it */
138     tmp = GUINT32_FROM_BE(hdr->magic);
139     if (tmp != AU_MAGIC)
140     {
141         ULOG_ERR("%s: wrong magic: %x, expected %x", G_STRFUNC, tmp, AU_MAGIC);
142         return -1;
143     }
144
145     *data_offset = GUINT32_FROM_BE(hdr->data_offset);
146     *data_size = GUINT32_FROM_BE(hdr->data_size);
147     
148     tmp = GUINT32_FROM_BE(hdr->encoding);
149     switch (tmp)
150     {
151         case AU_ENCODING_MULAW_8:
152             *format = FORMAT_PCMU;
153             break;
154             
155         case AU_ENCODING_ALAW_8:
156             *format = FORMAT_PCMA;
157             break;
158
159         case AU_ENCODING_LINEAR_16:
160             *format = FORMAT_PCM;
161             break;
162
163         default:
164             ULOG_ERR("%s: unsupported AU encoding %u", G_STRFUNC, tmp);
165             return -1;
166     }
167
168     *rate = GUINT32_FROM_BE(hdr->sample_rate);
169     *channels = GUINT32_FROM_BE(hdr->channels);
170
171     return read_tot;
172 }
173
174 gint 
175 au_copy_data(GnomeVFSHandle *to_handle, GnomeVFSHandle *from_handle, guint32 from_offset)
176 {
177     GnomeVFSResult res;
178     gpointer buffer;
179     gboolean ok = TRUE;
180     GnomeVFSFileSize read, written, written_tot = 0;
181
182     res = gnome_vfs_seek(from_handle,
183                          GNOME_VFS_SEEK_START,
184                          (GnomeVFSFileOffset) from_offset);
185
186     if (res != GNOME_VFS_OK)
187         return -1;
188
189     buffer = (gpointer) g_malloc0(COPY_BUFSIZE);
190
191     while (ok)
192     {
193         read = 0;
194         res = gnome_vfs_read(from_handle, 
195                 buffer,
196                 COPY_BUFSIZE,
197                 &read);
198
199         if (res != GNOME_VFS_OK)
200         {
201             ok = FALSE;
202             if (res != GNOME_VFS_ERROR_EOF)
203             {
204                 ULOG_ERR("%s: read failed: %s", G_STRFUNC, gnome_vfs_result_to_string(res));
205                 goto error;
206             }
207         }
208
209         if (read > 0)
210         {
211             res = gnome_vfs_write(to_handle,
212                     buffer,
213                     read,
214                     &written);
215
216             if (res != GNOME_VFS_OK)
217             {
218                 ULOG_ERR("%s: write failed: %s", G_STRFUNC, gnome_vfs_result_to_string(res));
219                 goto error;
220             }
221             if (read != written)
222             {
223                 ULOG_ERR("%s: read != written", G_STRFUNC);
224                 goto error;
225
226             }
227             written_tot += written;
228         }
229     }
230
231     g_free(buffer);
232     return written_tot;
233
234 error:
235     g_free(buffer);
236     return -1;
237
238 }
239
240 guint32 au_get_encoding(AudioFormat format)
241 {
242     switch (format)
243     {
244         case FORMAT_PCMU:
245             return AU_ENCODING_MULAW_8;
246             
247         case FORMAT_PCMA:
248             return AU_ENCODING_ALAW_8;
249
250         case FORMAT_PCM:
251             return AU_ENCODING_LINEAR_16;
252
253         default:
254             ULOG_ERR("%s: unsupported format for AU encoding %u", G_STRFUNC, format);
255             
256             return 0;
257     }
258     return 0;
259 }