LoginSignup
0
0

TypeScript/React/Tone.jsでダイアトニックコード進行トレーニングアプリを作る #1

Last updated at Posted at 2024-04-29

先人達が様々な音感トレーニングアプリを作成してくれているが、私が求めているトレーニング内容のものが見つからない。

音楽アプリの開発について調べたところTone.jsというライブラリが便利そうなので、TypeScript/Reactの勉強がてら自作してみることにした。音楽、プログラミングのトレーニングが出来て一石二鳥。

一旦メジャー・マイナーダイアトニックコードの表示・再生までは実装できた。
時間があるときにクイズ形式の機能も作成予定。

実際のアプリは下記URL。
keyと、メジャーかマイナーかを選択するとダイアトニックコード一覧が表示され、再生もできる。音の範囲は1オクターブのみにした。
結構アレなデザインだが、この辺りも今後勉強していければと思う。
https://haaan1234.github.io/DChordApp/

使ったもの

・VScode
・TypeScript
・Node.js  https://github.com/nodejs/node/blob/main/LICENSE
・React  Copyright (c) Meta Platforms, Inc. and affiliates.
・Tone.js  Copyright (c) 2014-2020 Yotam Mann
・MUI   Copyright © 2024 Material-UI.

前提

create react-app ~ --template typescript
などでプロジェクト作成まで済んでいること

各種インストール

MUI
yarn add @mui/material @emotion/react @emotion/styled

Tone.js
yarn add tone

実装

初めてTypecript/Reactに触れたが、親コンポーネント・子コンポーネント間のやり取りのイメージを掴むまで苦労した。。パッと作れるようにはなるまでは時間がかかりそう。

以下、まずは主要なソース全文。クリックで開けます。

App.tsx(親) 画面表示などの主要な処理実施
App.tsx
import { useState } from "react"
import { MakeDChords } from "./logic"
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import React from "react";
import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent } from "@mui/material";


export default function App() {
import { useState } from "react"
import { MakeDChords } from "./logic"
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import React from "react";
import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent } from "@mui/material";


export default function App() {
  //key選択状況を保持
  const [key, setKey] = React.useState(0);
  //keyが選択されたときに処理実行
  const keyChange = (event: SelectChangeEvent) => {
    setKey(Number(event.target.value as string));
  };

  //Maj/min選択状況を保持
  const [Mm, setMm] = React.useState(0);
  //Maj/minが選択されたときに処理実行
  const MmChange = (event: SelectChangeEvent) => {
    setMm(Number(event.target.value as string));
  };

  return (
    <Grid container
      direction="row"
      alignContent="center"
      justifyContent="center"
      sx={{ minHeight: "50vh" }}
    > <Grid item xs={0}>
        <FormControl fullWidth>
          <InputLabel id="key">key</InputLabel>
          <Select
            labelId="key"
            id="key"
            label="key"
            onChange={keyChange}
          >
            <MenuItem value={0}>C</MenuItem>
            <MenuItem value={1}>D</MenuItem>
            <MenuItem value={2}>D</MenuItem>
            <MenuItem value={3}>E</MenuItem>
            <MenuItem value={4}>E</MenuItem>
            <MenuItem value={5}>F</MenuItem>
            <MenuItem value={6}>G♭</MenuItem>
            <MenuItem value={7}>G</MenuItem>
            <MenuItem value={8}>A♭</MenuItem>
            <MenuItem value={9}>A</MenuItem>
            <MenuItem value={10}>B♭</MenuItem>
            <MenuItem value={11}>B</MenuItem>
          </Select>
        </FormControl>
      </Grid>
      <Grid item xs={0}>
        <FormControl>
          <InputLabel id="Mm">maj/min</InputLabel>
          <Select
            labelId="Mm"
            id="Mm"
            label="Mm"
            onChange={MmChange}  >
            <MenuItem value={0}>maj</MenuItem>
            <MenuItem value={1}>min</MenuItem>
          </Select>
        </FormControl>
        <MakeDChords dkey={key} Mm={Mm} />
      </Grid>
      <Grid item xs={12}>

      </Grid>
    </Grid>
  )
}
logic.tsx(子) 音階などを保持し、ダイアトニックコードを作成する。App.tsxに結果を返す
logic.tsx
import styled from "@emotion/styled";
import Button from "@mui/material/Button";
import ButtonGroup from "@mui/material/ButtonGroup";
import React, { MouseEventHandler } from "react"
import * as Tone from 'tone'

//鍵盤音(1オクターブのみ)
const tone = ["C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4"];

//メジャーダイアトニックコード
const MdChords = [
    ["C", "Dm", "Em", "F", "G", "Am", "Bm(♭5)"],
    ["D♭", "E♭m", "Fm", "G♭", "A♭", "B♭m", "Cm(♭5)"],
    ["D", "Em", "F#m", "G", "A", "Bm", "C#m(♭5)"],
    ["E♭", "Fm", "Gm", "A♭", "B♭", "Cm", "Dm(♭5)"],
    ["E", "F#m", "G#m", "A", "B", "C#m", "D#m(♭5)"],
    ["F", "Gm", "Am", "B♭", "C", "Dm", "Em(♭5)"],
    ["G♭", "A♭m", "B♭m", "C♭", "D♭", "E♭m", "Fm(♭5)"],
    ["G", "Am", "Bm", "C", "D", "Em", "F#m(♭5)"],
    ["A♭", "B♭m", "Cm", "D♭", "E♭", "Fm", "Gm(♭5)"],
    ["A", "Bm", "C#m", "D", "E", "F#m", "G#m(♭5)"],
    ["B♭", "Cm", "Dm", "E♭", "F", "Gm", "Am(♭5)"],
    ["B", "C#m", "D#m", "E", "F#", "G#m", "A#m(♭5)"]
]

//マイナーダイアトニックコード
const mDChords = [
    ["Cm", "Dm♭5", "E♭", "Fm", "Gm", "G#", "B♭"],
    ["D♭m", "E♭m♭5", "E", "F#m", "G#m", "A", "B"],
    ["Dm", "Em♭5", "F", "Gm", "Am", "B♭", "C"],
    ["E♭m", "Fm♭5", "F#", "G#m", "B♭m", "B", "C#"],
    ["Em", "F#m♭5", "G", "Am", "Bm", "C", "D"],
    ["Fm", "Gm♭5", "G#", "B♭m", "Cm", "C#", "E♭"],
    ["G♭m", "G#m♭5", "A", "Bm", "C#m", "D", "E"],
    ["Gm", "Am♭5", "B♭", "Cm", "Dm", "E♭", "F"],
    ["A♭m", "B♭m♭5", "B", "C#m", "E♭m", "E", "F#"],
    ["Am", "Bm♭5", "C", "Dm", "Em", "F", "G"],
    ["B♭m", "Cm♭5", "C#", "E♭m", "Fm", "F#", "G#"],
    ["Bm", "C#m♭5", "D", "Em", "F#m", "G", "A"]
]

//全ダイアトニックコード
const dChords: string[][][] = [
    MdChords,
    mDChords
]

type Props = {
    dkey: number,
    Mm: number
}

//音源接続
var synth = new Tone.PolySynth().toMaster();

//ボタン表記が大文字になるのを防ぐ
const TextButton = styled(Button)`
  text-transform: none;
`;


export const MakeDChords = (props: Props) => {
    if (props.Mm === 0) {
        var _chord = [tone[props.dkey], tone[props.dkey + 4], tone[props.dkey + 7]];
        var _Chord = [tone[props.dkey + 2], tone[props.dkey + 5], tone[props.dkey + 9]];
        var _chord = [tone[props.dkey + 4], tone[props.dkey + 7], tone[props.dkey + 11]];
        var _chord = [tone[props.dkey + 5], tone[props.dkey + 9], tone[props.dkey + 12]];
        var _chord = [tone[props.dkey + 7], tone[props.dkey + 11], tone[props.dkey + 14]];
        var _chord = [tone[props.dkey + 9], tone[props.dkey + 12], tone[props.dkey + 16]];
        var _chord = [tone[props.dkey + 11], tone[props.dkey + 14], tone[props.dkey + 17]];

        return (<ButtonGroup size="large" aria-label="Large button group">
            <TextButton key="one" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][0]}{"\n"}</TextButton>,
            <TextButton key="two" onClick={() => { synth.triggerAttackRelease(Ⅱ_Chord, '4n'); }}>{dChords[props.Mm][props.dkey][1]}{"\n"}Ⅱ</TextButton>,
            <TextButton key="three" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][2]}{"\n"}</TextButton>,
            <TextButton key="four" onClick={() => { synth.triggerAttackRelease(Ⅳ_chord, '4n'); }}>{dChords[props.Mm][props.dkey][3]}{"\n"}Ⅳ</TextButton>,
            <TextButton key="five" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][4]}{"\n"}</TextButton>,
            <TextButton key="six" onClick={() => { synth.triggerAttackRelease(Ⅵ_chord, '4n'); }}>{dChords[props.Mm][props.dkey][5]}{"\n"}Ⅵ</TextButton>,
            <TextButton key="seven" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][6]}{"\n"}</TextButton>
        </ButtonGroup>
        )
    } else {
        var _chord = [tone[props.dkey], tone[props.dkey + 3], tone[props.dkey + 7]];
        var _Chord = [tone[props.dkey + 2], tone[props.dkey + 5], tone[props.dkey + 8]];
        var _chord = [tone[props.dkey + 3], tone[props.dkey + 7], tone[props.dkey + 10]];
        var _chord = [tone[props.dkey + 5], tone[props.dkey + 8], tone[props.dkey + 12]];
        var _chord = [tone[props.dkey + 7], tone[props.dkey + 10], tone[props.dkey + 14]];
        var _chord = [tone[props.dkey + 8], tone[props.dkey + 12], tone[props.dkey + 15]];
        var _chord = [tone[props.dkey + 10], tone[props.dkey + 14], tone[props.dkey + 17]];

        return (
            <ButtonGroup size="large" aria-label="Large button group">
                <TextButton key="one" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][0]}{"\n"}</TextButton>,
                <TextButton key="two" onClick={() => { synth.triggerAttackRelease(Ⅱ_Chord, '4n'); }}>{dChords[props.Mm][props.dkey][1]}{"\n"}Ⅱ</TextButton>,
                <TextButton key="three" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][2]}{"\n"}</TextButton>,
                <TextButton key="four" onClick={() => { synth.triggerAttackRelease(Ⅳ_chord, '4n'); }}>{dChords[props.Mm][props.dkey][3]}{"\n"}Ⅳ</TextButton>,
                <TextButton key="five" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][4]}{"\n"}</TextButton>,
                <TextButton key="six" onClick={() => { synth.triggerAttackRelease(Ⅵ_chord, '4n'); }}>{dChords[props.Mm][props.dkey][5]}{"\n"}Ⅵ</TextButton>,
                <TextButton key="seven" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][6]}{"\n"}</TextButton>
            </ButtonGroup>
        )
    }
}

App.tsx

App.tsx
  //key選択状況を保持
  const [key, setKey] = React.useState(0);
  //keyが選択されたときに処理実行
  const keyChange = (event: SelectChangeEvent) => {
    setKey(Number(event.target.value as string));
  };

  //Maj/min選択状況を保持
  const [Mm, setMm] = React.useState(0);
  //Maj/minが選択されたときに処理実行
  const MmChange = (event: SelectChangeEvent) => {
    setMm(Number(event.target.value as string));
  };

key(調)とMm(メジャーキーかマイナーキーか)の選択状況を保持し、select状況が変更されるイベントが起きるたびに新しい選択をsetする。



App.tsx
 return (
    <Grid container
      direction="row"
      alignContent="center"
      justifyContent="center"
      sx={{ minHeight: "50vh" }}
    > <Grid item xs={0}>
        <FormControl fullWidth>
          <InputLabel id="key">key</InputLabel>
          <Select
            labelId="key"
            id="key"
            label="key"
            onChange={keyChange}
          >
            <MenuItem value={0}>C</MenuItem>
            <MenuItem value={1}>D</MenuItem>
            <MenuItem value={2}>D</MenuItem>
            <MenuItem value={3}>E</MenuItem>
            <MenuItem value={4}>E</MenuItem>
            <MenuItem value={5}>F</MenuItem>
            <MenuItem value={6}>G♭</MenuItem>
            <MenuItem value={7}>G</MenuItem>
            <MenuItem value={8}>A♭</MenuItem>
            <MenuItem value={9}>A</MenuItem>
            <MenuItem value={10}>B♭</MenuItem>
            <MenuItem value={11}>B</MenuItem>
          </Select>
        </FormControl>
      </Grid>
      <Grid item xs={0}>
        <FormControl>
          <InputLabel id="Mm">maj/min</InputLabel>
          <Select
            labelId="Mm"
            id="Mm"
            label="Mm"
            onChange={MmChange}  >
            <MenuItem value={0}>maj</MenuItem>
            <MenuItem value={1}>min</MenuItem>
          </Select>
        </FormControl>
        <MakeDChords dkey={key} Mm={Mm} />
      </Grid>
      <Grid item xs={12}>

      </Grid>
    </Grid>

  )

keyとMmのselectボックスを表示している。
onChange={keyChange}
onChange={MmChange}
でselect状況が更新された際の処理を呼んでいる。


<MakeDChords dkey={key} Mm={Mm} />
では、logic.tsx(子)に親としてkeyとMmが何を選択されているかのvalueをpropsで送る。

logic.tsx

logic.tsx
//鍵盤音(1オクターブのみ)
const tone = ["C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4"];

//メジャーダイアトニックコード
const MdChords = [
    ["C", "Dm", "Em", "F", "G", "Am", "Bm(♭5)"],
    ["D♭", "E♭m", "Fm", "G♭", "A♭", "B♭m", "Cm(♭5)"],
    ["D", "Em", "F#m", "G", "A", "Bm", "C#m(♭5)"],
    ["E♭", "Fm", "Gm", "A♭", "B♭", "Cm", "Dm(♭5)"],
    ["E", "F#m", "G#m", "A", "B", "C#m", "D#m(♭5)"],
    ["F", "Gm", "Am", "B♭", "C", "Dm", "Em(♭5)"],
    ["G♭", "A♭m", "B♭m", "C♭", "D♭", "E♭m", "Fm(♭5)"],
    ["G", "Am", "Bm", "C", "D", "Em", "F#m(♭5)"],
    ["A♭", "B♭m", "Cm", "D♭", "E♭", "Fm", "Gm(♭5)"],
    ["A", "Bm", "C#m", "D", "E", "F#m", "G#m(♭5)"],
    ["B♭", "Cm", "Dm", "E♭", "F", "Gm", "Am(♭5)"],
    ["B", "C#m", "D#m", "E", "F#", "G#m", "A#m(♭5)"]
]

//マイナーダイアトニックコード
const mDChords = [
    ["Cm", "Dm♭5", "E♭", "Fm", "Gm", "G#", "B♭"],
    ["D♭m", "E♭m♭5", "E", "F#m", "G#m", "A", "B"],
    ["Dm", "Em♭5", "F", "Gm", "Am", "B♭", "C"],
    ["E♭m", "Fm♭5", "F#", "G#m", "B♭m", "B", "C#"],
    ["Em", "F#m♭5", "G", "Am", "Bm", "C", "D"],
    ["Fm", "Gm♭5", "G#", "B♭m", "Cm", "C#", "E♭"],
    ["G♭m", "G#m♭5", "A", "Bm", "C#m", "D", "E"],
    ["Gm", "Am♭5", "B♭", "Cm", "Dm", "E♭", "F"],
    ["A♭m", "B♭m♭5", "B", "C#m", "E♭m", "E", "F#"],
    ["Am", "Bm♭5", "C", "Dm", "Em", "F", "G"],
    ["B♭m", "Cm♭5", "C#", "E♭m", "Fm", "F#", "G#"],
    ["Bm", "C#m♭5", "D", "Em", "F#m", "G", "A"]
]

//全ダイアトニックコード
const dChords: string[][][] = [
    MdChords,
    mDChords
]

toneにC4~B4の音を繰り返し入れている。
1オクターブ分の鍵盤が横にずっと並んでいると考えればイメージしやすいかも。

ダイアトニックコード名に関してはコードの通りで、配列に入れて管理。

logic.tsx

type Props = {
    dkey: number,
    Mm: number
}

//音源接続
var synth = new Tone.PolySynth().toMaster();

//ボタン表記が大文字になるのを防ぐ
const TextButton = styled(Button)`
  text-transform: none;
`;

type Propsで、App.tsx(親)から渡されてくるkeyとMmの型を指定。ここをちゃんと合わせないとエラーになる。

Tone.PolySynth().toMaster()でTone.jsの音を使用する。
今回はコード(和音)なのでPolySynth()を使用

MUIがボタン名の英字を勝手に大文字にしてくるので、スタイル指定用のコード記載。

logic.tsx

export const MakeDChords = (props: Props) => {
    if (props.Mm === 0) {
        var _chord = [tone[props.dkey], tone[props.dkey + 4], tone[props.dkey + 7]];
        var _Chord = [tone[props.dkey + 2], tone[props.dkey + 5], tone[props.dkey + 9]];
        var _chord = [tone[props.dkey + 4], tone[props.dkey + 7], tone[props.dkey + 11]];
        var _chord = [tone[props.dkey + 5], tone[props.dkey + 9], tone[props.dkey + 12]];
        var _chord = [tone[props.dkey + 7], tone[props.dkey + 11], tone[props.dkey + 14]];
        var _chord = [tone[props.dkey + 9], tone[props.dkey + 12], tone[props.dkey + 16]];
        var _chord = [tone[props.dkey + 11], tone[props.dkey + 14], tone[props.dkey + 17]];

        return (<ButtonGroup size="large" aria-label="Large button group">
            <TextButton key="one" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][0]}{"\n"}</TextButton>,
            <TextButton key="two" onClick={() => { synth.triggerAttackRelease(Ⅱ_Chord, '4n'); }}>{dChords[props.Mm][props.dkey][1]}{"\n"}Ⅱ</TextButton>,
            <TextButton key="three" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][2]}{"\n"}</TextButton>,
            <TextButton key="four" onClick={() => { synth.triggerAttackRelease(Ⅳ_chord, '4n'); }}>{dChords[props.Mm][props.dkey][3]}{"\n"}Ⅳ</TextButton>,
            <TextButton key="five" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][4]}{"\n"}</TextButton>,
            <TextButton key="six" onClick={() => { synth.triggerAttackRelease(Ⅵ_chord, '4n'); }}>{dChords[props.Mm][props.dkey][5]}{"\n"}Ⅵ</TextButton>,
            <TextButton key="seven" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][6]}{"\n"}</TextButton>
        </ButtonGroup>
        )
    } else {
        var _chord = [tone[props.dkey], tone[props.dkey + 3], tone[props.dkey + 7]];
        var _Chord = [tone[props.dkey + 2], tone[props.dkey + 5], tone[props.dkey + 8]];
        var _chord = [tone[props.dkey + 3], tone[props.dkey + 7], tone[props.dkey + 10]];
        var _chord = [tone[props.dkey + 5], tone[props.dkey + 8], tone[props.dkey + 12]];
        var _chord = [tone[props.dkey + 7], tone[props.dkey + 10], tone[props.dkey + 14]];
        var _chord = [tone[props.dkey + 8], tone[props.dkey + 12], tone[props.dkey + 15]];
        var _chord = [tone[props.dkey + 10], tone[props.dkey + 14], tone[props.dkey + 17]];

        return (
            <ButtonGroup size="large" aria-label="Large button group">
                <TextButton key="one" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][0]}{"\n"}</TextButton>,
                <TextButton key="two" onClick={() => { synth.triggerAttackRelease(Ⅱ_Chord, '4n'); }}>{dChords[props.Mm][props.dkey][1]}{"\n"}Ⅱ</TextButton>,
                <TextButton key="three" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][2]}{"\n"}</TextButton>,
                <TextButton key="four" onClick={() => { synth.triggerAttackRelease(Ⅳ_chord, '4n'); }}>{dChords[props.Mm][props.dkey][3]}{"\n"}Ⅳ</TextButton>,
                <TextButton key="five" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][4]}{"\n"}</TextButton>,
                <TextButton key="six" onClick={() => { synth.triggerAttackRelease(Ⅵ_chord, '4n'); }}>{dChords[props.Mm][props.dkey][5]}{"\n"}Ⅵ</TextButton>,
                <TextButton key="seven" onClick={() => { synth.triggerAttackRelease(_chord, '4n'); }}>{dChords[props.Mm][props.dkey][6]}{"\n"}</TextButton>
            </ButtonGroup>
        )
    }
}


ここでApp.tsxに結果をreturn
export const MakeDChords = (props: Props) => {
exportで他のファイルにインポートできるよう宣言。
props: PropsでApp.tsx(親)からのpropsを受け取る。コロンの後のPropsで型指定。
if (props.Mm === 0) {でメジャーかマイナーか判定


var Ⅰ_chord ~ var Ⅶ_chordで現在選ばれているkeyを基にtone配列から音を取り出してダイアトニックコードを作成する。ここはプログラムというよりは音楽よりの話。

<TextButton key="one" onClick={() => { synth.triggerAttackRelease(Ⅰ_chord, '4n'); }}>{dChords[props.Mm][props.dkey][0]}{"\n"}Ⅰ</TextButton>,
onClick={() => { synth.triggerAttackRelease(Ⅰ_chord, '4n'); }}でボタンクリックされたときにコードをどのくらいの長さ再生するかを記載。
{dChords[props.Mm][props.dkey][0]}{"\n"}Ⅰでボタン名にダイアトニックコード名を入れる。※{"\n"}は改行

以上。

まとめ

実装としては以上になります。何かのお役に立てたら幸いです。

今後も機能追加したら記事を書いてみようと思う。
↓ソース
https://github.com/haaan1234/DChordApp

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0