summaryrefslogtreecommitdiffstats
path: root/firmware/talk.c
blob: d02a4efd7f1d1ebbeb8d455e181bcc8a09be52bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2004 Jörg Hohensohn
 *
 * This module collects the Talkbox and voice UI functions.
 * (Talkbox reads directory names from mp3 clips called thumbnails,
 *  the voice UI lets menus and screens "talk" from a voicefont in memory.
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include <stdio.h>
#include <stddef.h>
#include "file.h"
#include "buffer.h"
#include "kernel.h"
#include "mp3_playback.h"
#include "mpeg.h"
#include "talk.h"
extern void bitswap(unsigned char *data, int length); /* no header for this */

/***************** Constants *****************/

#define VOICEFONT_FILENAME "/.rockbox/voicefont"


/***************** Data types *****************/

struct clip_entry /* one entry of the index table */
{
    int offset; /* offset from start of voicefont file */
    int size; /* size of the clip */
};

struct voicefont /* file format of our "voicefont" */
{
    int version; /* version of the voicefont */
    int headersize; /* size of the header, =offset to index */
    int id_max; /* number of clips contained */
    struct clip_entry index[]; /* followed by the index table */
    /* and finally the bitswapped mp3 clips, not visible here */
};


/***************** Globals *****************/

static unsigned char* p_thumbnail; /* buffer for thumbnail */
static long size_for_thumbnail; /* leftover buffer size for it */
static struct voicefont* p_voicefont; /* loaded voicefont */
static bool has_voicefont; /* a voicefont file is present */
static bool is_playing; /* we're currently playing */



/***************** Private implementation *****************/

static int load_voicefont(void)
{
    int fd;
    int size;

    p_voicefont = NULL; /* indicate no voicefont if we fail below */

    fd = open(VOICEFONT_FILENAME, O_RDONLY);
    if (fd < 0) /* failed to open */
    {
        p_voicefont = NULL; /* indicate no voicefont */
        has_voicefont = false; /* don't try again */
        return 0;
    }

    size = read(fd, mp3buf, mp3end - mp3buf);
    if (size > 1000
        && ((struct voicefont*)mp3buf)->headersize
           == offsetof(struct voicefont, index))
    {
        p_voicefont = (struct voicefont*)mp3buf;

        /* thumbnail buffer is the remaining space behind */
        p_thumbnail = mp3buf + size;
        p_thumbnail += (int)p_thumbnail % 2; /* 16-bit align */
        size_for_thumbnail = mp3end - p_thumbnail;
        
        /* max. DMA size, fixme */
        if (size_for_thumbnail > 0xFFFF)
            size_for_thumbnail = 0xFFFF;
    }
    else
    {
        has_voicefont = false; /* don't try again */
    }
    close(fd);

    return size;
}


/* called in ISR context if mp3 data got consumed */
void mp3_callback(unsigned char** start, int* size)
{
    (void)start; /* unused parameter, avoid warning */
    *size = 0; /* end of data */
    is_playing = false;
    mp3_play_stop(); /* fixme: should be done by caller */
}

/***************** Public implementation *****************/

void talk_init(void)
{
    has_voicefont = true; /* unless we fail later, assume we have one */
    talk_buffer_steal();
}


/* somebody else claims the mp3 buffer, e.g. for regular play/record */
int talk_buffer_steal(void)
{
    p_voicefont = NULL; /* indicate no voicefont (trashed) */
    p_thumbnail = mp3buf; /*  whole space for thumbnail */
    size_for_thumbnail = mp3end - mp3buf;
    /* max. DMA size, fixme */
    if (size_for_thumbnail > 0xFFFF)
        size_for_thumbnail = 0xFFFF;
    return 0;
}


/* play a voice ID from voicefont */
int talk_id(int id, bool block)
{
    int clipsize;

    if (mpeg_status()) /* busy, buffer in use */
        return -1; 

    if (p_voicefont == NULL && has_voicefont)
        load_voicefont(); /* reload needed */

    if (p_voicefont == NULL) /* still no voices? */
        return -1;

    if (id >= p_voicefont->id_max)
        return -1;

    clipsize = p_voicefont->index[id].size;
    if (clipsize == 0) /* clip not included in voicefont */
        return -1;
    
    is_playing = true;
    mp3_play_data(mp3buf + p_voicefont->index[id].offset, 
        clipsize, mp3_callback);
    mp3_play_pause(true); /* kickoff audio */

    while(block && is_playing)
        sleep(1);

    return 0;
}


/* play a thumbnail from file */
int talk_file(char* filename, bool block)
{
    int fd;
    int size;

    if (mpeg_status()) /* busy, buffer in use */
        return -1; 

    if (p_thumbnail == NULL || size_for_thumbnail <= 0)
        return -1;

    fd = open(filename, O_RDONLY);
    if (fd < 0) /* failed to open */
    {
        return 0;
    }

    size = read(fd, p_thumbnail, size_for_thumbnail);
    close(fd);

    /* ToDo: find audio, skip ID headers and trailers */

    if (size)
    {
        bitswap(p_thumbnail, size);
        is_playing = true;
        mp3_play_data(p_thumbnail, size, mp3_callback);
        mp3_play_pause(true); /* kickoff audio */

        while(block && is_playing)
            sleep(1);
    }

    return size;
}