• Ingen resultater fundet

3.Realisation

3.3. Text-to-speech module

3.3.1. Main structure

3.3.1.3. Implementation

3.3.1 Main structure 45

• TTSDecodeDecodeAudioFile: This module needs the file content that should be decoded and returns the decoded information. An error code is return if the file can’t be decoded. The decoded information is the same as the TTSPlayPlay function needs. That is the PCM data, sample rate, number of channels and number of samples.

• TTSFetchGetAudioFileData: This function needs a word and it will return the content of the corresponding file. If the file can’t be found an error code is returned.

• TTSSplitSetText: This function is used to initialize the TTSSplit component. It needs the text and returns the number of words found. If an error occurs an error code is returned.

• TTSSplitGetNextWord: This function just returns the next word in the text given to TTSSplitSetText. When all the words have been returned an error is returned.

• TTSMainPlayText: This function is the interface to the text-to-speech module. It needs the text which is then parsed on to TTSSplitSetText. It also needs the volume and whether to turn the speaker on or off. This is parsed on to TTSPlayPlay.

Illustration 20.Implemented structure of the text-to-speech module TTSMainPlay takes four arguments. textP, languageP, volume and enableSpeaker.

textP is a pointer to a string with the text that needs to be played. languageP is a pointer to string with a language code. For a list of the supported languages see the implementation of TTSFetch (page 58). volume is a number that sets the output volume. The range goes from 0 to 63. 0 is maximum volume and 63 is the minimum.

enableSpeaker is a one or a zero that indicates if the speaker should be enabled or not.

int32_t TTSMainPlayText( uint8_t * textP, uint8_t * languageP, uint16_t volume,

int32_t enableSpeaket );

TTSMainPlay prototype

Common data structures

The information about the audio needs to be transferred from TTSDecode to TTSMain and from there to TTSPlay. Instead of having to transfer all the individual values a structure is made that has these values. A pointer to the structure can then be transferred around. This also makes it possible to give the pointer to

3.3.1 Main structure 47

TTSDecodeDecodeAudioFile and then let TTSDecodeDecodeAudioFile fill the structure. This way the normal return value can be the error code. The number of samples is not static and needs to be dynamically allocated. Therefore the structure will just have a pointer to the memory area where the samples are located. The structure is defined like this:

typedef struct audioS{

uint32_t sampleRate; // samples per second

uint32_t length; // number of bytes used for the samples uint16_t * samplesP; // location of the samples

uint16_t channels; // 1=mono, 2=stereo } audioT;

This is a structure with the information needed to play the audio.

The number of bits used by the samples can be different from file to file. The pointer is chosen to be a pointer to a 16 bit value. This is the maximum number of bits supported by the AC’97 controller. The other data types are based on the format they have in a wave file.

The file data also needs to be moved around. This means a lot of bytes and a number that tells how many bytes there are. This can be useful in other places too. Every where a function needs to return a pointer to a memory area of unknown size it also needs to return the size. The structure is implemented like the audio structure but is called bufferT instead of audioT. It has a field called len and a pointer to an unsigned 8 bit value called dataP.

Often when the memory used for the structures is freed the memory areas pointed to by the pointers in the structures will also need to be freed. Instead of having to free both every where, a free function is made for each structure type. These functions will then free both the areas pointed to by the pointers and the structures themselves. This also means if a new pointer is added in one of the structures the only changes will be in the free function and not everywhere the structures are used. To make sure the structures are initialized with zero, functions are made that will allocate memory for the structure and write zeros in that area. They then return pointers to the allocated areas. The reason for initializing the structures with zero is that the pointers in the structure must be set to NULL. If they point to a random place in memory it will give problems when the free function tries to free that area. The structures and the functions are made in the files TTSCommon.c and TTSCommon.h.

These are the prototypes for the functions:

audioT * TTSCommonCreateAudioStruct( void );

bufferT * TTSCommonCreateBufferStruct( void );

void TTSCommonFreeAudioStruct( void * audioP );

void TTSCommonFreeBufferStruct( void * bufferP );

As one can see the free functions uses a void pointer as argument. The reason for this will come later.

Circular linked list

TTSMain needs a way to store all the audio structures returned from

TTSDecodeDecode until they are played. For this a list is used. TTSSplit also needs a list for the individual words. In both cases new elements are added to the end and when the list is done all the elements are needed one at the time from the start of the list. In both cases a circular linked list is perfect for the job. In a circular linked list the last node has a pointer to the first node. Instead of a pointer to the first node the program has a pointer to the last node in the list. New nodes are therefore fast to insert at the end because the program doesn’t have to traverse the entire list. To make an

implementation that can be used by both TTSMain and TTSSplit the node in the list just has a pointer to the next node and a void pointer that can point to whatever value or structure that needs to be stored in the list.

Illustration 21. This illustration shows how the list is made up of nodes that have pointers to the next node in the list and the last has a pointer to the first node. Each

node has a void pointer to whatever object that needs to be stored in the list.

The circular linked list is implemented in the files CircularLinkedList.c and CircularLinkedList.h.. A function (CircularLinkedListAppend) is made to append objects to a list. It takes a pointer to the list and a pointer to whatever object that needs to be appended. A new node is then created and the object pointer is set to the same as the object pointer given as an argument. The new node is inserted ad the end of the list.

A pointer to the new node is then returned. That pointer will then be the new list pointer. If the program doesn’t have a list yet the function is just called with null as the list pointer. Then the new element will be made so it point to itself.

To make it easier to free the memory used by the nodes in a list, a function

(CircularLinkedListFreeNodes) has been made that traverse the list and frees all the nodes. Another function (CircularLinkedListFreeObjects) has been made that does almost the same but instead of freeing the nodes it frees the objects pointed to in the nodes. This however creates a problem. There is no way to know how to free the objects. To solve this CircularLinkedListFreeObjects takes a pointer to a function that

3.3.1 Main structure 49

can free the objects. This function is then called for each node in the list with that node’s object pointer as an argument.

Illustration 22. As mentioned the object pointers in the nodes are void pointers. The list is not aware of what type of objects it contains. That is why

TTSCommonFreeAudioStruct (and other free functions) must take a void pointer as an argument.