Skip to content

phonlab-tcd/abair.ie

Β 
Β 

Repository files navigation

Providing State-of-the-Art Irish Speech and Language Technologies to the Public since 2011

Description

This is the source code for the main website of the Abair project developed by the Phonetics and Speech Laboratory at Trinity College Dublin.

Aims of the Website

The codebase was developed for two audiences:

  • Public
  • Developers

The public must be able to easily and reliably access the speech technologies and applications developed by the Abair group.

New developers (often students) must experience a frictionless onboarding process where they can quickly understand the codebase and become active contributors.

Design Philosophy

The design for the Abair website follows the KISS principle.

Code should be minimal, modular, with clear documentation and arranged in a logical structure.

The tools employed, e.g. React, Supabase, are widely used, well maintained and with comprehensive documentation.

Tools, Libraries, Packages...

This template was used to initally structure the project (not all features are used).

Abair is built using the following:

Language

Build Tool

Front-end Framework

Router

UI Framework

Component Library

State Management

Back End as a Service (BaaS)

Dev Tools

Getting Started 1: Front End

Clone project from Github:

git clone https://github.com/JohnSloan8/abair.git

Install dependencies:

npm install

Run dev environment:

npm run dev

Build:

npm run build

Run a preview of the build from the dist folder in http:

npm run preview

Same with https:

npm run https-preview

Getting Started 2: Back End

Supabase is used as the back-end for this site. This includes the following open-source tools out of the box:

You will need to request access to the Abair project from the project manager. Once granted, create a .env file in the root of the project for environment variables:

/.env

VITE_SUPABASE_URL=https://gheiwof...
VITE_SUPABASE_ANON_KEY=fhIend9wqb...

Front End File Structure

The directories in the src folder are:

πŸ“‚ config
πŸ“‚ display
πŸ“‚ error-handling
πŸ“‚ models
πŸ“‚ routes
πŸ“‚ services
πŸ“‚ store

Display

Each page on Abair consists of πŸ“‚ components, πŸ“‚ controllers, and πŸ“‚ sections. Each is explained below

components

These are independent, reusable pieces of code. They are hosted in a seperate npm package which is built using Storybook and can be accessed here.

  • Material UI is the styling library utilised for components
  • Recoil State is always passed to components as arguments.
  • React useState may be utilised within the component.
  • Components tend to have a lot of parameters as many argments are passed.

Example: The AbSlider component has 8 parameters, 5 of which are predefined.

const AbSlider = ({
  min = 0,
  max = 100,
  value,
  step = 1,
  onChange,
  icon,
  iconSize = 'medium',
  color = 'secondary.main',
}: AbSliderProps) => {
  return (
    <Stack
      spacing={2}
      py={{ sm: 2, xs: 0.5 }}
      sx={{ width: '100%' }}
      direction="row"
      alignItems="center"
      justifyContent="center"
    >
      {icon !== undefined && <SvgIcon component={icon} sx={{ color: color }} fontSize={iconSize} />}
      <Slider
        aria-label="Speed"
        valueLabelDisplay="auto"
        value={value}
        min={min}
        step={step}
        max={max}
        sx={{ color: color }}
        onChange={onChange}
      />
    </Stack>
  );
};

controllers

Controllers interact with the state library. They access (and update) state, passing it as arguments to components if necessary.

  • They never use parameters.
  • May or may not return HTML.

Example: The SynthesisSpeed Controller subscribes to the Recoil useSynthesisSpeed() and useSynthesisVoiceIndex() functions to get (and set) state variables. These are then passed as arguments to the AbSlider component (described above).

/src/controllers/Synthesis/SynthesisSpeed/SynthesisSpeed.tsx

...
const SynthesisSpeed = () => {
  const { synthesisSpeed, setSynthesisSpeed } = useSynthesisSpeed();
  const { synthesisVoiceIndex } = useSynthesisVoiceIndex();

  return synthesisVoiceIndex !== -1 ? (
    <AbSlider
      min={0.5}
      value={synthesisSpeed}
      max={1.5}
      onChange={(e) => setSynthesisSpeed(parseFloat((e.target as HTMLInputElement).value))}
      step={0.1}
      icon={SpeedIcon}
    />
  ) : null;
};

sections

A section is a block of reusable code which may contain a number of controllers and/or components, e.g. Headers and Footers.

pages

A page may contain any number of sections, controllers, and components.

Example: The SynthesisSpeed controller is used on the abair.ie/synthesis page, alongside the SynthesisVoiceButtons, Gender, SynthesisPitch, and SynthesisModel controllers.

/src/pages/SpeechSynthesis/SpeechSynthesis.tsx

...
<Grid>
  <Box minHeight={'69px'}>
    <SynthesisVoiceButtons />
  </Box>
  <GenderChoices />
  <Box width={'90%'}>
    <Box minHeight={{ sm: '62px', xs: '52px' }}>
      <SynthesisSpeed />
    </Box>
    <Box minHeight={{ sm: '62px', xs: '52px' }}>
      <SynthesisPitch />
    </Box>
  </Box>
  <Box minHeight={'77px'}>
    <SynthesisModel />
  </Box>
</Grid>
...

Models

Contains type descriptions for the objects used in the project.

interface synthesisVoiceModel {
  name: string;
  gender: string;
  locale: string;
  mode?: string;
  shortCode: string;
  voices: string[];
  pitchRange: number[];
  speedRange: number[];
  pitch: number;
  speed: number;
}

Routes

All routes for the project are listed in the src/routes/index.ts file, with redering handled by React Router taking place in src/routes/Pages/Pages.tsx. A new route can be added by appending to the list:

const routes: Routes = {
    [Pages.Home]: {
        component: asyncComponentLoader(() => import('@/display/pages/Home')),
        path: '/',
        title: 'home',
        icon: HomeIcon,
        showInSidebar: true,
    },
    [Pages.SpeechSynthesis]: {
        component: asyncComponentLoader(() => import('@/display/pages/SpeechSynthesis')),
        path: '/speech-synthesis',
        ...

Services

All calls to external APIs take place here. These involve requests to the Abair speech API for synthesis and recognition, and to Supabase for data stored there (e.g. profile information, publications, images, request history etc.)

Store

All state management is managed here. Each piece of state is defined with a custom React hook exported.

/src/store/synthesis/audio.ts

...
const synthesisAudioState = atom<string>({
  key: 'synthesis-audio-state',
  default: '',
});

const useSynthesisAudio = () => {
  const [synthesisAudio, setSynthesisAudio] = useRecoilState(synthesisAudioState);
  return { synthesisAudio, setSynthesisAudio };
};
...

As much as possible, state operations should be done in the store rather than in components (selecting, filtering etc.). For example, determining whether the text box for typing a synthesis request is empty or not:

/src/store/synthesis/audio.ts

...
const isSynthesisAudioEmpty = selector({
  key: 'synthesis-audio-empty-state',
  get: ({ get }) => {
    const data = get(synthesisAudioState);
    return data.length > 0 ? false : true;
  },
});
...

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 99.2%
  • Other 0.8%