From e1283624ad3302a3b3834c0001c368492ab2d257 Mon Sep 17 00:00:00 2001 From: tainakanchu Date: Sat, 29 Jul 2023 02:02:35 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=E7=A7=BB=E5=8B=95=E5=B9=B3=E5=9D=87?= =?UTF-8?q?=E3=81=AE=E3=83=8F=E3=83=B3=E3=83=89=E3=83=A9=E3=82=92=E3=83=AA?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/_utils/bpmCalculator.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/app/_utils/bpmCalculator.ts b/app/_utils/bpmCalculator.ts index 7352d83..b3f702a 100644 --- a/app/_utils/bpmCalculator.ts +++ b/app/_utils/bpmCalculator.ts @@ -4,6 +4,11 @@ const MAX_PAST_TIME = 20 * 1000; // これ以上の間隔でのデータは考慮しない const DIFF_THRESHOLD = 3000; +const simpleMovingAverageHandler = + (count: number) => + (acc: number, cur: number): number => + acc + cur / count; + type Return = { value: number | null; sd: number | null; @@ -31,15 +36,14 @@ export const bpmCalculator: (dateList: Date[]) => Return = (dateList) => { // 一定の秒数以上の差分は考慮しない .filter((diff) => diff < DIFF_THRESHOLD); - // TODO: 平均値からあまりにも外れてるデータも考慮しない - // 必要数のデータがない場合は null を返す if (filteredDiffList.length < 1) return emptyReturn; // 差分の平均値を単純移動平均で計算 - const average = - filteredDiffList.reduce((acc, cur) => acc + cur, 0) / - filteredDiffList.length; + const average = filteredDiffList.reduce( + simpleMovingAverageHandler(filteredDiffList.length), + 0 + ); const σ = calculateStandardDeviation(filteredDiffList); const bpm = 60000 / average; @@ -62,9 +66,10 @@ export const bpmCalculator: (dateList: Date[]) => Return = (dateList) => { if (filteredDiffList2.length < 8) return tmpReturn; // 改めて平均値を計算 - const average2 = - filteredDiffList2.reduce((acc, cur) => acc + cur, 0) / - filteredDiffList2.length; + const average2 = filteredDiffList2.reduce( + simpleMovingAverageHandler(filteredDiffList2.length), + 0 + ); // 改めて標準偏差を計算 const sd = calculateStandardDeviation(filteredDiffList2); From 79976f2fc62152aa92b518ee8e64d1599d3ca2a8 Mon Sep 17 00:00:00 2001 From: tainakanchu Date: Sat, 29 Jul 2023 22:11:12 +0900 Subject: [PATCH 2/7] add analytics package --- package.json | 1 + pnpm-lock.yaml | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/package.json b/package.json index 8f621be..26ee5f2 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@types/node": "20.4.5", "@types/react": "18.2.17", "@types/react-dom": "18.2.7", + "@vercel/analytics": "^1.0.1", "autoprefixer": "10.4.14", "eslint": "8.45.0", "eslint-config-next": "13.4.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1db3bd..dc25282 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: '@types/react-dom': specifier: 18.2.7 version: 18.2.7 + '@vercel/analytics': + specifier: ^1.0.1 + version: 1.0.1 autoprefixer: specifier: 10.4.14 version: 10.4.14(postcss@8.4.27) @@ -1680,6 +1683,10 @@ packages: eslint-visitor-keys: 3.4.1 dev: false + /@vercel/analytics@1.0.1: + resolution: {integrity: sha512-Ux0c9qUfkcPqng3vrR0GTrlQdqNJ2JREn/2ydrVuKwM3RtMfF2mWX31Ijqo1opSjNAq6rK76PwtANw6kl6TAow==} + dev: false + /@webassemblyjs/ast@1.11.6: resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==} dependencies: From 823b07465233e244b82c1fc596a21e7fd7c053cb Mon Sep 17 00:00:00 2001 From: tainakanchu Date: Sat, 29 Jul 2023 22:19:52 +0900 Subject: [PATCH 3/7] add analytics --- app/layout.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/layout.tsx b/app/layout.tsx index 1b71b19..fde897d 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,8 @@ import "./globals.css"; import type { Metadata } from "next"; import { BIZ_UDPGothic } from "next/font/google"; +import { Analytics } from "@vercel/analytics/react"; + import Pwa from "./_components/Pwa"; const bizUd = BIZ_UDPGothic({ @@ -29,6 +31,7 @@ export default function RootLayout({ {children} + ); From 2e48847f3f8022306e1b526389f5dddb7c994d60 Mon Sep 17 00:00:00 2001 From: tainakanchu Date: Sat, 29 Jul 2023 23:34:24 +0900 Subject: [PATCH 4/7] =?UTF-8?q?bpm=E8=A8=88=E7=AE=97=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=82=92hooks=E3=81=AB=E5=88=87=E3=82=8A=E5=87=BA=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/_components/BpmComponent.tsx | 71 ++++++++----------- app/_hooks/index.ts | 1 + app/_hooks/useBpmCalculator.ts | 46 ++++++++++++ app/_types/BpmConvertSetting.ts | 9 +++ .../{bpmCalculator.ts => calculateBpm.ts} | 15 ++-- app/_utils/index.ts | 2 +- 6 files changed, 95 insertions(+), 49 deletions(-) create mode 100644 app/_hooks/index.ts create mode 100644 app/_hooks/useBpmCalculator.ts create mode 100644 app/_types/BpmConvertSetting.ts rename app/_utils/{bpmCalculator.ts => calculateBpm.ts} (94%) diff --git a/app/_components/BpmComponent.tsx b/app/_components/BpmComponent.tsx index 3366a26..dc47465 100644 --- a/app/_components/BpmComponent.tsx +++ b/app/_components/BpmComponent.tsx @@ -3,23 +3,30 @@ import React from "react"; import { BpmButton } from "./BpmButton"; import { SubBpmComponent } from "./SubBpmComponent"; -import { bpmCalculator } from "../_utils"; +import { useBpmCalculator } from "../_hooks"; +import { BpmConvertSetting } from "../_types/BpmConvertSetting"; type Props = {}; -export const BpmComponent: React.FC = ({}) => { - const [dateList, setDateList] = React.useState([]); - - const handleButtonClick = React.useCallback(() => { - setDateList((dateList) => [...dateList, new Date()]); - }, []); +const bpmConvertSettings: BpmConvertSetting[] = [ + { + numerator: 1, + denominator: 2, + }, + { + numerator: 3, + denominator: 4, + }, + { + numerator: 4, + denominator: 3, + }, +]; - const bpm = React.useMemo(() => { - return bpmCalculator(dateList); - }, [dateList]); +export const BpmComponent: React.FC = ({}) => { + const { handleAddTimeData, handleClearTimeData, bpm, convertedBpmList } = + useBpmCalculator({ bpmConvertSettings }); - // sdの値に応じてbpmの文字の色を変える - // sdの値は bpm に対しての割合で評価する const { sd: _sd, value } = bpm; const sd = _sd ?? 50; @@ -49,44 +56,28 @@ export const BpmComponent: React.FC = ({}) => { } }, [sd]); - const halfBpm = React.useMemo(() => { - return value ? value / 2 : undefined; - }, [value]); - - const threeFourthBpm = React.useMemo(() => { - return value ? (value * 3) / 4 : undefined; - }, [value]); - - const fourThirdBpm = React.useMemo(() => { - return value ? (value * 4) / 3 : undefined; - }, [value]); - return (
- +

TAP

-

- {bpm.value?.toFixed(1) ?? "🎶"} -

+

{value?.toFixed(1) ?? "🎶"}

- - - + {convertedBpmList.map((convertedBpm) => { + return ( + + ); + })}
diff --git a/app/_hooks/index.ts b/app/_hooks/index.ts new file mode 100644 index 0000000..bd935dc --- /dev/null +++ b/app/_hooks/index.ts @@ -0,0 +1 @@ +export * from "./useBpmCalculator"; diff --git a/app/_hooks/useBpmCalculator.ts b/app/_hooks/useBpmCalculator.ts new file mode 100644 index 0000000..500f5d7 --- /dev/null +++ b/app/_hooks/useBpmCalculator.ts @@ -0,0 +1,46 @@ +import React from "react"; +import { calculateBpm } from "../_utils"; +import { BpmConvertSetting } from "../_types/BpmConvertSetting"; + +/** + * BPMを計算するフック + * + * @param setting BPMを変換する設定 + * @returns BPMを計算するフック + */ +export const useBpmCalculator = (setting: { + bpmConvertSettings: BpmConvertSetting[]; +}) => { + const [dateList, setDateList] = React.useState([]); + + const handleAddTimeData = React.useCallback(() => { + setDateList((dateList) => [...dateList, new Date()]); + }, []); + + const handleClearTimeData = React.useCallback(() => { + setDateList([]); + }, []); + + const bpm = React.useMemo(() => { + return calculateBpm(dateList); + }, [dateList]); + + const convertedBpmList = React.useMemo(() => { + return setting.bpmConvertSettings.map((setting) => { + const label: string = `${setting.numerator}/${setting.denominator}`; + return { + label, + value: bpm.value + ? (bpm.value * setting.numerator) / setting.denominator + : null, + }; + }); + }, [bpm.value, setting]); + + return { + handleAddTimeData, + handleClearTimeData, + bpm, + convertedBpmList, + }; +}; diff --git a/app/_types/BpmConvertSetting.ts b/app/_types/BpmConvertSetting.ts new file mode 100644 index 0000000..e944655 --- /dev/null +++ b/app/_types/BpmConvertSetting.ts @@ -0,0 +1,9 @@ +/** + * BPMを変換する設定 + */ +export type BpmConvertSetting = { + /** 分子 */ + denominator: number; + /** 分母 */ + numerator: number; +}; diff --git a/app/_utils/bpmCalculator.ts b/app/_utils/calculateBpm.ts similarity index 94% rename from app/_utils/bpmCalculator.ts rename to app/_utils/calculateBpm.ts index b3f702a..84e607d 100644 --- a/app/_utils/bpmCalculator.ts +++ b/app/_utils/calculateBpm.ts @@ -9,15 +9,14 @@ const simpleMovingAverageHandler = (acc: number, cur: number): number => acc + cur / count; -type Return = { - value: number | null; - sd: number | null; -}; - /** - * + * BPMを計算する + * @param dateList 記録された時間のリスト */ -export const bpmCalculator: (dateList: Date[]) => Return = (dateList) => { +export const calculateBpm: (dateList: Date[]) => { + value: number | null; + sd: number | null; +} = (dateList) => { // 閾値以内に記録されたデータだけ使う const now = new Date(); const past = new Date(now.getTime() - MAX_PAST_TIME); @@ -82,7 +81,7 @@ export const bpmCalculator: (dateList: Date[]) => Return = (dateList) => { }; }; -const emptyReturn: Return = { +const emptyReturn = { value: null, sd: null, }; diff --git a/app/_utils/index.ts b/app/_utils/index.ts index 015d947..ee6c8bc 100644 --- a/app/_utils/index.ts +++ b/app/_utils/index.ts @@ -1 +1 @@ -export * from "./bpmCalculator"; +export * from "./calculateBpm"; From 09492d985eceb700270dad2b70a73f41c3a6b3ba Mon Sep 17 00:00:00 2001 From: tainakanchu Date: Sat, 29 Jul 2023 23:42:07 +0900 Subject: [PATCH 5/7] =?UTF-8?q?sd=20=E3=81=AB=E5=BF=9C=E3=81=98=E3=81=9F?= =?UTF-8?q?=E8=89=B2=E3=82=92=E8=BF=94=E3=81=99=E5=87=A6=E7=90=86=E3=82=92?= =?UTF-8?q?hooks=E3=81=AB=E5=88=87=E3=82=8A=E5=87=BA=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/_components/BpmComponent.tsx | 31 +++--------------------------- app/_hooks/index.ts | 1 + app/_hooks/useAccuracyColor.ts | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 app/_hooks/useAccuracyColor.ts diff --git a/app/_components/BpmComponent.tsx b/app/_components/BpmComponent.tsx index dc47465..fc66479 100644 --- a/app/_components/BpmComponent.tsx +++ b/app/_components/BpmComponent.tsx @@ -3,7 +3,7 @@ import React from "react"; import { BpmButton } from "./BpmButton"; import { SubBpmComponent } from "./SubBpmComponent"; -import { useBpmCalculator } from "../_hooks"; +import { useAccuracyColor, useBpmCalculator } from "../_hooks"; import { BpmConvertSetting } from "../_types/BpmConvertSetting"; type Props = {}; @@ -27,34 +27,9 @@ export const BpmComponent: React.FC = ({}) => { const { handleAddTimeData, handleClearTimeData, bpm, convertedBpmList } = useBpmCalculator({ bpmConvertSettings }); - const { sd: _sd, value } = bpm; + const { sd, value } = bpm; - const sd = _sd ?? 50; - - // 5刻みぐらいで色を変える - const bpmColor = React.useMemo(() => { - if (sd < 30) { - return "text-green-500"; - } else if (sd < 35) { - return "text-green-400"; - } else if (sd < 40) { - return "text-green-300"; - } else if (sd < 45) { - return "text-yellow-200"; - } else if (sd < 50) { - return "text-yellow-100"; - } else if (sd < 55) { - return "text-red-100"; - } else if (sd < 60) { - return "text-red-200"; - } else if (sd < 65) { - return "text-red-300"; - } else if (sd < 70) { - return "text-red-400"; - } else { - return "text-red-500"; - } - }, [sd]); + const bpmColor = useAccuracyColor(sd ?? 50); return (
diff --git a/app/_hooks/index.ts b/app/_hooks/index.ts index bd935dc..735959b 100644 --- a/app/_hooks/index.ts +++ b/app/_hooks/index.ts @@ -1 +1,2 @@ +export * from "./useAccuracyColor"; export * from "./useBpmCalculator"; diff --git a/app/_hooks/useAccuracyColor.ts b/app/_hooks/useAccuracyColor.ts new file mode 100644 index 0000000..284ab28 --- /dev/null +++ b/app/_hooks/useAccuracyColor.ts @@ -0,0 +1,33 @@ +import React from "react"; + +/** + * 値の正確度に応じた色を返す + */ +export const useAccuracyColor = (sd: number): string => { + // 5刻みぐらいで色を変える + const bpmColor = React.useMemo(() => { + if (sd < 30) { + return "text-green-500"; + } else if (sd < 35) { + return "text-green-400"; + } else if (sd < 40) { + return "text-green-300"; + } else if (sd < 45) { + return "text-yellow-200"; + } else if (sd < 50) { + return "text-yellow-100"; + } else if (sd < 55) { + return "text-red-100"; + } else if (sd < 60) { + return "text-red-200"; + } else if (sd < 65) { + return "text-red-300"; + } else if (sd < 70) { + return "text-red-400"; + } else { + return "text-red-500"; + } + }, [sd]); + + return bpmColor; +}; From 2e8f83ffe9856a15db2bcc5822602686a783b498 Mon Sep 17 00:00:00 2001 From: tainakanchu Date: Sun, 30 Jul 2023 00:05:50 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=E7=9F=AD=E6=99=82=E9=96=93=E3=81=AB?= =?UTF-8?q?=E5=A4=A7=E9=87=8F=E3=81=AE=E3=82=BF=E3=83=83=E3=83=97=E3=81=8C?= =?UTF-8?q?=E3=81=82=E3=81=A3=E3=81=9F=E3=81=A8=E3=81=8D=E3=81=ABInfinity?= =?UTF-8?q?=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=A6=E3=81=97?= =?UTF-8?q?=E3=81=BE=E3=81=86=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #15 --- app/_utils/calculateBpm.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/_utils/calculateBpm.ts b/app/_utils/calculateBpm.ts index 84e607d..6aa2a3d 100644 --- a/app/_utils/calculateBpm.ts +++ b/app/_utils/calculateBpm.ts @@ -61,15 +61,15 @@ export const calculateBpm: (dateList: Date[]) => { (diff) => Math.abs(diff - average) < σ ); - // データが減りすぎの時は第一段階の計算結果を返す - if (filteredDiffList2.length < 8) return tmpReturn; - // 改めて平均値を計算 const average2 = filteredDiffList2.reduce( simpleMovingAverageHandler(filteredDiffList2.length), 0 ); + // average2 がゼロの時は第一段階の計算結果を返す + if (average2 === 0) return tmpReturn; + // 改めて標準偏差を計算 const sd = calculateStandardDeviation(filteredDiffList2); From 3ce3348b2a3de9881c630e0cf6d8eb968c5a4cca Mon Sep 17 00:00:00 2001 From: tainakanchu Date: Sun, 30 Jul 2023 00:21:08 +0900 Subject: [PATCH 7/7] =?UTF-8?q?=F0=9F=9A=80=20bump=200.3.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- public/manifest.webmanifest | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 26ee5f2..b89e3af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bpm-calculator-for-dj", - "version": "0.3.0", + "version": "0.3.1", "private": true, "scripts": { "dev": "next dev", diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest index f530024..4600b2f 100644 --- a/public/manifest.webmanifest +++ b/public/manifest.webmanifest @@ -1,7 +1,7 @@ { "short_name": "BPM Calc", "name": "BPM calculator for DJ", - "version": "0.3.0", + "version": "0.3.1", "theme_color": "#080808", "background_color": "#080808", "display": "standalone",