summaryrefslogtreecommitdiff
path: root/firmware/talk.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/talk.c')
-rw-r--r--firmware/talk.c209
1 files changed, 209 insertions, 0 deletions
diff --git a/firmware/talk.c b/firmware/talk.c
new file mode 100644
index 0000000000..d02a4efd7f
--- /dev/null
+++ b/firmware/talk.c
@@ -0,0 +1,209 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 Jörg Hohensohn
11 *
12 * This module collects the Talkbox and voice UI functions.
13 * (Talkbox reads directory names from mp3 clips called thumbnails,
14 * the voice UI lets menus and screens "talk" from a voicefont in memory.
15 *
16 * All files in this archive are subject to the GNU General Public License.
17 * See the file COPYING in the source tree root for full license agreement.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24#include <stdio.h>
25#include <stddef.h>
26#include "file.h"
27#include "buffer.h"
28#include "kernel.h"
29#include "mp3_playback.h"
30#include "mpeg.h"
31#include "talk.h"
32extern void bitswap(unsigned char *data, int length); /* no header for this */
33
34/***************** Constants *****************/
35
36#define VOICEFONT_FILENAME "/.rockbox/voicefont"
37
38
39/***************** Data types *****************/
40
41struct clip_entry /* one entry of the index table */
42{
43 int offset; /* offset from start of voicefont file */
44 int size; /* size of the clip */
45};
46
47struct voicefont /* file format of our "voicefont" */
48{
49 int version; /* version of the voicefont */
50 int headersize; /* size of the header, =offset to index */
51 int id_max; /* number of clips contained */
52 struct clip_entry index[]; /* followed by the index table */
53 /* and finally the bitswapped mp3 clips, not visible here */
54};
55
56
57/***************** Globals *****************/
58
59static unsigned char* p_thumbnail; /* buffer for thumbnail */
60static long size_for_thumbnail; /* leftover buffer size for it */
61static struct voicefont* p_voicefont; /* loaded voicefont */
62static bool has_voicefont; /* a voicefont file is present */
63static bool is_playing; /* we're currently playing */
64
65
66
67/***************** Private implementation *****************/
68
69static int load_voicefont(void)
70{
71 int fd;
72 int size;
73
74 p_voicefont = NULL; /* indicate no voicefont if we fail below */
75
76 fd = open(VOICEFONT_FILENAME, O_RDONLY);
77 if (fd < 0) /* failed to open */
78 {
79 p_voicefont = NULL; /* indicate no voicefont */
80 has_voicefont = false; /* don't try again */
81 return 0;
82 }
83
84 size = read(fd, mp3buf, mp3end - mp3buf);
85 if (size > 1000
86 && ((struct voicefont*)mp3buf)->headersize
87 == offsetof(struct voicefont, index))
88 {
89 p_voicefont = (struct voicefont*)mp3buf;
90
91 /* thumbnail buffer is the remaining space behind */
92 p_thumbnail = mp3buf + size;
93 p_thumbnail += (int)p_thumbnail % 2; /* 16-bit align */
94 size_for_thumbnail = mp3end - p_thumbnail;
95
96 /* max. DMA size, fixme */
97 if (size_for_thumbnail > 0xFFFF)
98 size_for_thumbnail = 0xFFFF;
99 }
100 else
101 {
102 has_voicefont = false; /* don't try again */
103 }
104 close(fd);
105
106 return size;
107}
108
109
110/* called in ISR context if mp3 data got consumed */
111void mp3_callback(unsigned char** start, int* size)
112{
113 (void)start; /* unused parameter, avoid warning */
114 *size = 0; /* end of data */
115 is_playing = false;
116 mp3_play_stop(); /* fixme: should be done by caller */
117}
118
119/***************** Public implementation *****************/
120
121void talk_init(void)
122{
123 has_voicefont = true; /* unless we fail later, assume we have one */
124 talk_buffer_steal();
125}
126
127
128/* somebody else claims the mp3 buffer, e.g. for regular play/record */
129int talk_buffer_steal(void)
130{
131 p_voicefont = NULL; /* indicate no voicefont (trashed) */
132 p_thumbnail = mp3buf; /* whole space for thumbnail */
133 size_for_thumbnail = mp3end - mp3buf;
134 /* max. DMA size, fixme */
135 if (size_for_thumbnail > 0xFFFF)
136 size_for_thumbnail = 0xFFFF;
137 return 0;
138}
139
140
141/* play a voice ID from voicefont */
142int talk_id(int id, bool block)
143{
144 int clipsize;
145
146 if (mpeg_status()) /* busy, buffer in use */
147 return -1;
148
149 if (p_voicefont == NULL && has_voicefont)
150 load_voicefont(); /* reload needed */
151
152 if (p_voicefont == NULL) /* still no voices? */
153 return -1;
154
155 if (id >= p_voicefont->id_max)
156 return -1;
157
158 clipsize = p_voicefont->index[id].size;
159 if (clipsize == 0) /* clip not included in voicefont */
160 return -1;
161
162 is_playing = true;
163 mp3_play_data(mp3buf + p_voicefont->index[id].offset,
164 clipsize, mp3_callback);
165 mp3_play_pause(true); /* kickoff audio */
166
167 while(block && is_playing)
168 sleep(1);
169
170 return 0;
171}
172
173
174/* play a thumbnail from file */
175int talk_file(char* filename, bool block)
176{
177 int fd;
178 int size;
179
180 if (mpeg_status()) /* busy, buffer in use */
181 return -1;
182
183 if (p_thumbnail == NULL || size_for_thumbnail <= 0)
184 return -1;
185
186 fd = open(filename, O_RDONLY);
187 if (fd < 0) /* failed to open */
188 {
189 return 0;
190 }
191
192 size = read(fd, p_thumbnail, size_for_thumbnail);
193 close(fd);
194
195 /* ToDo: find audio, skip ID headers and trailers */
196
197 if (size)
198 {
199 bitswap(p_thumbnail, size);
200 is_playing = true;
201 mp3_play_data(p_thumbnail, size, mp3_callback);
202 mp3_play_pause(true); /* kickoff audio */
203
204 while(block && is_playing)
205 sleep(1);
206 }
207
208 return size;
209}