diff --git a/packages/audiodocs/docs/fundamentals/lets-make-some-noise.mdx b/packages/audiodocs/docs/fundamentals/lets-make-some-noise.mdx
index 0ff007d6..8ef6f3ab 100644
--- a/packages/audiodocs/docs/fundamentals/lets-make-some-noise.mdx
+++ b/packages/audiodocs/docs/fundamentals/lets-make-some-noise.mdx
@@ -108,3 +108,17 @@ import LetsMakeSomeNoiseSrc from '!!raw-loader!@site/src/examples/LetsMakeSomeNo
In web environment you can use `decodeAudioDataSource` directly on the asset url, without the need to download it first.
:::
+
+## Summary
+
+In this guide, we have learned how to create a simple audio player using `AudioContext` and `AudioBufferSourceNode` as well as how we can load audio data from a remote source. To sum up:
+
+- `AudioContext` is the main object that controls the audio graph.
+- `decodeAudioDataSource` method can be used to load audio data from a local audio source in form of `AudioBuffer`.
+- `AudioBufferSourceNode` can be used to any `AudioBuffer`.
+- In order to hear the sounds, we need to connect the source node to the destination node exposed by `AudioContext`.
+- We can control the playback of the sound using `start` and `stop` methods of the `AudioBufferSourceNode` (And other source nodes which we show later).
+
+## What's next?
+
+In [the next section](/fundamentals/making-a-piano-keyboard) we will learn more about how the audio graph works, what are audio params and how we can use them to create a simple piano keyboard.
diff --git a/packages/audiodocs/docs/fundamentals/making-a-piano-keyboard.mdx b/packages/audiodocs/docs/fundamentals/making-a-piano-keyboard.mdx
new file mode 100644
index 00000000..0db740a6
--- /dev/null
+++ b/packages/audiodocs/docs/fundamentals/making-a-piano-keyboard.mdx
@@ -0,0 +1,362 @@
+---
+sidebar_position: 3
+---
+
+# Making a piano keyboard
+
+In this section, we will use some of the core audio api interfaces to create a simple piano keyboard. We will learn what is an `AudioParam`, how to use it to change the pitch of the sound.
+
+## Base application
+
+Like in previous example, we will start with simple app with couple of buttons so we just don't need to worry about the UI later.
+You can just copy and paste the code below to your project.
+
+```tsx
+import React from 'react';
+import { View, Text, Pressable } from 'react-native';
+
+type KeyName = 'A' | 'B' | 'C' | 'D' | 'E';
+
+interface ButtonProps {
+ keyName: KeyName;
+ onPressIn: (key: KeyName) => void;
+ onPressOut: (key: KeyName) => void;
+}
+
+const Button = ({ onPressIn, onPressOut, keyName }: ButtonProps) => (
+ onPressIn(keyName)}
+ onPressOut={() => onPressOut(keyName)}
+ style={({ pressed }) => ({
+ margin: 4,
+ padding: 12,
+ borderRadius: 2,
+ backgroundColor: pressed ? '#d2e6ff' : '#abcdef',
+ })}
+ >
+ {`${keyName}`}
+
+);
+
+export default function SimplePiano() {
+ const onKeyPressIn = (which: KeyName) => {};
+ const onKeyPressOut = (which: KeyName) => {};
+
+ return (
+
+ {Keys.map((key) => (
+
+ ))}
+
+ );
+}
+```
+
+## Create audio context and preload the data
+
+Like previously, we will need to preload the audio files in order to be able to play them. Using the interfaces we already know, we will download them and store in the memory using good old `useRef` hook.
+
+First comes the import section and list of the sources we will use, also lets help ourselves with type shorthand for partial record:
+
+```tsx
+import {
+ GainNode,
+ AudioBuffer,
+ AudioContext,
+ AudioBufferSourceNode,
+} from 'react-native-audio-api';
+import * as FileSystem from 'expo-file-system';
+
+/* ... */
+
+type PR = Partial>;
+
+const sourceList: PR = {
+ A: 'https://software-mansion-labs.github.io/react-native-audio-api/audio/sounds/C4.mp3',
+ C: 'https://software-mansion-labs.github.io/react-native-audio-api/audio/sounds/Ds4.mp3',
+ E: 'https://software-mansion-labs.github.io/react-native-audio-api/audio/sounds/Fs4.mp3',
+};
+```
+
+then we will want to fetch the audio files and store them. We want the audio data to be available to play as soon as possible,
+so we will use the `useEffect` hook to download them and store in the useRef hook for simplicity.
+
+```tsx
+export default function SimplePiano() {
+ const audioContextRef = useRef(null);
+ const bufferMapRef = useRef>({});
+
+ useEffect(() => {
+ if (!audioContextRef.current) {
+ audioContextRef.current = new AudioContext();
+ }
+
+ Object.entries(sourceList).forEach(async ([key, source]) => {
+ bufferListRef.current[key as KeyName] = await FileSystem.downloadAsync(
+ url,
+ `${FileSystem.documentDirectory}/${key}.mp3`
+ ).then(({ uri }) => audioContextRef.current!.decodeAudioDataSource(uri));
+ });
+ }, []);
+}
+```
+
+## Playing the sounds
+
+Now it is finally time to play the sounds, but still nothing new here. We will use the `AudioBufferSourceNode` and simply play the buffers.
+
+```tsx
+export default function SimplePiano() {
+ const onKeyPressIn = (which: KeyName) => {
+ const audioContext = audioContextRef.current!;
+ const buffer = bufferMapRef.current[which];
+
+ const source = new AudioBufferSourceNode(audioContext, {
+ buffer,
+ });
+
+ source.connect(audioContext.destination);
+ source.start();
+ };
+}
+```
+
+Great! But a lot of things are a bit off here:
+
+- We are not stopping the sound when the button is released, which is kind of the way piano should work, right? 🙃
+- You have probably noticed in the previous section, but we are missing sounds for keys 'B' and 'D'.
+
+Let's see how we can tackle that using the audio api. We will take them down one by one, ready?
+
+## Key release
+
+To stop the sound when keys are released, we will need to store somewhere source nodes, in order to be able to call `stop` on them later. Like for audio context lets use `useRef` hook.
+
+```tsx
+const playingNotesRef = useRef>({});
+```
+
+Now we need to modify the `onKeyPressIn` function a bit
+
+```tsx
+const onKeyPressIn = (which: KeyName) => {
+ const audioContext = audioContextRef.current!;
+ const buffer = bufferMapRef.current[which];
+
+ const source = new AudioBufferSourceNode(audioContext, {
+ buffer,
+ });
+
+ source.connect(audioContext.destination);
+ source.start();
+
+ playingNotesRef.current[which] = source;
+};
+```
+
+And finally we can implement the `onKeyPressOut` function
+
+```tsx
+const onKeyPressOut = (which: KeyName) => {
+ const source = playingNotesRef.current[which];
+ if (source) {
+ source.stop();
+ }
+};
+```
+
+And they stop on release, just as we wanted. But if we hold the keys for a short time, it sounds a bit strange. Also have You noticed that the sound is simply cut off when we release the key? 🤔
+It leave a bit unpleasant feeling, right? So let's try to make it a bit more smooth.
+
+## Envelopes ✉️
+
+We will start from the end this time, and finally we will use new type of audio node - `GainNode` :tada:
+`GainNode` is a simple node that can change the volume of any node (or nodes) connected to it. `GainNode` has a single element called `AudioParam` which name is also `gain`..., a bit lame but we have to live with that.
+
+## What is an AudioParam?
+
+An `AudioParam` is an interface, that controls various aspects of most audio nodes, like volume (in `GainNode` described above), pan or frequency. It allows to control them over time, so we can make smooth transitions and complex audio effects.
+For our use case, we are interested in two methods of an audio param: `setValueAtTime` and `exponentialRampToValueAtTime`.
+
+- `setValueAtTime(value: number, time: number)` - method is as simple as it sounds, it sets the given value at the time specified in the second argument.
+- `exponentialRampToValueAtTime(value: number, time: number)` - method is a bit more complex, it changes the value using exponential curve to the value passed as first argument at the time specified in the second one. It starts as soon as preceding change is finished. (f.e. `setValueAtTime`).
+
+So, how we can use that for our piano?
+The title of one of the previous sections might ring a bell 🔔.
+Envelope is term widely used in music and sound engineering, it describes how a sound changes over time (sounds similar to `AudioParam`, doesn't it?).
+The most common way of describing an envelope is ADSR (please don't mistake it with ASMR 🙂). This acronym stands for: **attack**, **decay**, **sustain** and **release**.
+
+- **Attack** - time it takes for the sound to reach its peak volume from the beginning.
+- **Decay** - time it takes for the sound to reach the sustain level after the peak volume.
+- **Sustain** - volume level that the sound will stay at until the key is released.
+- **Release** - time it takes for the sound to fade out after the key is released.
+
+![ADSR Example](/img/ADSR.svg)
+
+You can read more about envelopes and ADSR on [Wikipedia]().
+
+## Implementing the envelope
+
+With all the knowledge we have gathered, let's get back to the code. In our `onKeyPressIn` function, besides creating the source node, we will create a `GainNode` which will stand in the middle between the source and destination nodes, which will be our envelope.
+We want to implement the **attack** in `onKeyPressIn` function, and **release** in `onKeyPressOut`. In order to be able to access the envelope in both functions we will have to store it somewhere, so let's modify the `playingNotesRef` introduced earlier.
+Also lets not forget about the issue with short key presses, we will address that, by enforcing minimal duration of the sound to one second (As it works nicely with the samples we have 😉).
+
+First comes the types:
+
+```tsx
+interface PlayingNote {
+ source: AudioBufferSourceNode;
+ envelope: GainNode;
+ startedAt: number;
+}
+```
+
+and the `useRef` hook:
+
+```tsx
+const playingNotesRef = useRef>({});
+```
+
+Now we can modify the `onKeyPressIn` function:
+
+```tsx
+const onKeyPressIn = (which: KeyName) => {
+ const audioContext = audioContextRef.current!;
+ const buffer = bufferMapRef.current[which];
+ const tNow = audioContext.currentTime;
+
+ const source = aCtx.createBufferSource();
+ source.buffer = buffer;
+
+ const envelope = aCtx.createGain();
+
+ source.connect(envelope);
+ envelope.connect(audioContext.destination);
+
+ envelope.gain.setValueAtTime(0.001, tNow);
+ envelope.gain.exponentialRampToValueAtTime(1, tNow + 0.1);
+
+ source.start(tNow);
+ playingNotesRef.current[which] = { source, envelope, startedAt: tNow };
+};
+```
+
+and the `onKeyPressOut` function:
+
+```tsx
+const onKeyPressOut = (which: KeyName) => {
+ const audioContext = audioContextRef.current!;
+ const { source, envelope, startedAt } = playingNotesRef.current[which];
+
+ const tStop = Math.max(audioContext.currentTime, startedAt + 1);
+
+ envelope.gain.exponentialRampToValueAtTime(0.0001, tStop + 0.08);
+ envelope.gain.setValueAtTime(0, tStop + 0.09);
+ source.stop(tStop + 0.1);
+
+ playingNotesRef.current[which] = undefined;
+};
+```
+
+And it finally sounds smooth and nice. But what about decay and sustain phases? Both are done by the audio samples themselves, so we don't need to worry about them. To be honest, same goes for the attack phase, but we have implemented it for the sake of this guide. 🙂
+So the only missing piece left is doing something about the missing sample files for 'B' and 'D' keys. What we can do about that?
+
+## Tampering with the playback rate
+
+`AudioBufferSourceNode` also has its own `AudioParam`, it is called `playbackRate` as the title suggests. It allows to change the speed of the playback of the audio buffer.
+Yay! nice. But how can we use that to make the missing keys sound? I will keep this one short, as this guide is already quite long so lets wrap up!
+
+When we change the speed of some sound, it will also change it's pitch (frequency), so we can use that to make the missing keys sound.
+Each piano key has it's own dominant frequency (f.e. the frequency of the `A4` key is `440Hz`), we can check frequency behind each key, calculate the ratio between them and use it to change the playback rate of the buffers we have.
+
+![Piano keys frequencies](/img/frequencies-on-piano.jpg)
+
+for our example, lets use those frequencies as base of our calculations:
+
+```tsx
+const noteToFrequency = {
+ A: 261.626, // real piano middle C
+ B: 277.193, // Db
+ C: 311.127, // Eb
+ D: 329.628, // E
+ E: 369.994, // Gb
+};
+```
+
+First we need to find closest key to the missing one, we can do it in simple for loop:
+
+```tsx
+function getClosest(key: KeyName) {
+ let closestKey = 'A';
+ let minDiff = noteToFrequency.A - noteToFrequency[key];
+
+ for (const sourcedKey of Object.keys(sourceList)) {
+ const diff = noteToFrequency[sourcedKey] - noteToFrequency[key];
+
+ if (Math.abs(diff) < Math.abs(minDiff)) {
+ minDiff = diff;
+ closestKey = sourcedKey;
+ }
+ }
+
+ return closestKey;
+}
+```
+
+Now we just use the function in `onKeyPressIn` when the buffer is not found and change the playback rate for the source node:
+
+```tsx
+const onKeyPressIn = (which: KeyName) => {
+ let buffer = bufferListRef.current[which];
+ const aCtx = audioContextRef.current;
+ let playbackRate = 1;
+
+ if (!buffer) {
+ const closestKey = getClosest(which);
+ const closestBuffer = bufferMapRef.current[closestKey];
+ playbackRate = noteToFrequency[closestKey] / noteToFrequency[which];
+ }
+
+ const source = aCtx.createBufferSource();
+ const envelope = aCtx.createGain();
+ source.buffer = buffer;
+
+ // rest of the code remains the same
+};
+```
+
+## Final results
+
+As previously, you can see the final results in the live example below with full source code.
+
+import InteractiveExample from '@site/src/components/InteractiveExample';
+import SimplePiano from '@site/src/examples/SimplePiano/Component';
+import SimplePianoSrc from '!!raw-loader!@site/src/examples/SimplePiano/Source';
+
+
+
+## Summary
+
+In this guide we have learned how to create a simple piano keyboard with help of the gain node and audio params. To sum up:
+
+- `AudioParam` is an interface that provides ways to control various aspects of audio nodes over time.
+- `GainNode` is a simple node that can change the volume of any node connected to it.
+- `AudioBufferSourceNode` has param called `playbackRate` that allows to change the speed of the playback of the audio buffer and change pitch of the sounds.
+- We can use `GainNode` to create envelopes and make the sound transitions more smooth and pleasant.
+- We have learned how we can use the audio api in react environment in more production environment like scenario.
+
+## What's next?
+
+I don't know, give yourself a pat on the back, You've earned it! We will come with more guides soon, stay tunned! 🎉
diff --git a/packages/audiodocs/docusaurus.config.js b/packages/audiodocs/docusaurus.config.js
index f76eaff7..15ec3fa1 100644
--- a/packages/audiodocs/docusaurus.config.js
+++ b/packages/audiodocs/docusaurus.config.js
@@ -60,6 +60,7 @@ const config = {
markdown: {
mermaid: true,
},
+ themes: ['@docusaurus/theme-mermaid'],
themeConfig: {
// Replace with your project's social card
diff --git a/packages/audiodocs/package.json b/packages/audiodocs/package.json
index 18aee19f..f9408f48 100644
--- a/packages/audiodocs/package.json
+++ b/packages/audiodocs/package.json
@@ -24,6 +24,7 @@
"@docusaurus/core": "^2.4.3",
"@docusaurus/plugin-debug": "^2.4.3",
"@docusaurus/preset-classic": "^2.4.3",
+ "@docusaurus/theme-mermaid": "^2.4.3",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@mdx-js/react": "^1.6.22",
diff --git a/packages/audiodocs/src/examples/SimplePiano/Component.tsx b/packages/audiodocs/src/examples/SimplePiano/Component.tsx
new file mode 100644
index 00000000..c55db784
--- /dev/null
+++ b/packages/audiodocs/src/examples/SimplePiano/Component.tsx
@@ -0,0 +1,164 @@
+import {
+ GainNode,
+ AudioBuffer,
+ AudioContext,
+ AudioBufferSourceNode,
+} from 'react-native-audio-api';
+import { View, Text, Pressable } from 'react-native';
+import React, { FC, useEffect, useRef } from 'react';
+
+type KeyName = 'A' | 'B' | 'C' | 'D' | 'E';
+
+type PRecord = Partial>;
+
+interface ButtonProps {
+ keyName: KeyName;
+ onPressIn: (key: KeyName) => void;
+ onPressOut: (key: KeyName) => void;
+}
+
+interface PlayingNote {
+ source: AudioBufferSourceNode;
+ envelope: GainNode;
+ startedAt: number;
+}
+
+const Keys = ['A', 'B', 'C', 'D', 'E'] as const;
+
+const sourceList: Partial> = {
+ A: '/react-native-audio-api/audio/sounds/C4.mp3',
+ C: '/react-native-audio-api/audio/sounds/Ds4.mp3',
+ E: '/react-native-audio-api/audio/sounds/Fs4.mp3',
+};
+
+const noteToFrequency = {
+ A: 261.626,
+ B: 277.193,
+ C: 311.127,
+ D: 329.628,
+ E: 369.994,
+};
+
+function getClosest(key: KeyName) {
+ let closestKey = 'A';
+ let minDiff = noteToFrequency.A - noteToFrequency[key];
+
+ for (const sourcedKey of Object.keys(sourceList)) {
+ const diff = noteToFrequency[sourcedKey] - noteToFrequency[key];
+
+ if (Math.abs(diff) < Math.abs(minDiff)) {
+ minDiff = diff;
+ closestKey = sourcedKey;
+ }
+ }
+
+ return closestKey;
+}
+
+const Button = ({ onPressIn, onPressOut, keyName }: ButtonProps) => (
+ onPressIn(keyName)}
+ onPressOut={() => onPressOut(keyName)}
+ style={({ pressed }) => ({
+ margin: 4,
+ padding: 12,
+ borderRadius: 2,
+ backgroundColor: pressed ? '#d2e6ff' : '#abcdef',
+ })}
+ >
+ {`${keyName}`}
+
+);
+
+const SimplePiano: FC = () => {
+ const audioContextRef = useRef(null);
+ const bufferListRef = useRef>({});
+ const playingNotesRef = useRef>({});
+
+ const onKeyPressIn = (which: KeyName) => {
+ let buffer = bufferListRef.current[which];
+ const aCtx = audioContextRef.current;
+ let playbackRate = 1;
+
+ if (!aCtx) {
+ return;
+ }
+
+ if (!buffer) {
+ const closestKey = getClosest(which);
+ buffer = bufferListRef.current[closestKey];
+ playbackRate = noteToFrequency[which] / noteToFrequency[closestKey];
+ }
+
+ const source = aCtx.createBufferSource();
+ const envelope = aCtx.createGain();
+ source.buffer = buffer;
+ source.playbackRate.value = playbackRate;
+
+ const tNow = aCtx.currentTime;
+
+ envelope.gain.setValueAtTime(0.001, tNow);
+ envelope.gain.exponentialRampToValueAtTime(1, tNow + 0.01);
+
+ source.connect(envelope);
+ envelope.connect(aCtx.destination);
+
+ source.start(tNow);
+ playingNotesRef.current[which] = { source, envelope, startedAt: tNow };
+ };
+
+ const onKeyPressOut = (which: KeyName) => {
+ const { source, envelope, startedAt } = playingNotesRef.current[which];
+
+ const aCtx = audioContextRef.current;
+
+ if (!source || !envelope || !aCtx) {
+ return;
+ }
+
+ const tNow = Math.max(aCtx.currentTime, startedAt + 1);
+
+ envelope.gain.exponentialRampToValueAtTime(0.0001, tNow + 0.08);
+ envelope.gain.setValueAtTime(0, tNow + 0.09);
+ source.stop(tNow + 0.1);
+
+ playingNotesRef.current[which] = undefined;
+ };
+
+ useEffect(() => {
+ if (!audioContextRef.current) {
+ audioContextRef.current = new AudioContext();
+ }
+
+ Object.entries(sourceList).forEach(async ([key, url]) => {
+ bufferListRef.current[key as KeyName] =
+ await audioContextRef.current!.decodeAudioDataSource(url);
+ });
+
+ return () => {
+ audioContextRef.current?.close();
+ };
+ }, []);
+
+ return (
+
+ {Keys.map((key) => (
+
+ ))}
+
+ );
+};
+
+export default SimplePiano;
diff --git a/packages/audiodocs/src/examples/SimplePiano/Source.tsx b/packages/audiodocs/src/examples/SimplePiano/Source.tsx
new file mode 100644
index 00000000..25b7265d
--- /dev/null
+++ b/packages/audiodocs/src/examples/SimplePiano/Source.tsx
@@ -0,0 +1,167 @@
+import {
+ GainNode,
+ AudioBuffer,
+ AudioContext,
+ AudioBufferSourceNode,
+} from 'react-native-audio-api';
+import * as FileSystem from 'expo-file-system';
+import { View, Text, Pressable } from 'react-native';
+import React, { FC, useEffect, useRef } from 'react';
+
+type KeyName = 'A' | 'B' | 'C' | 'D' | 'E';
+
+type PRecord = Partial>;
+
+interface ButtonProps {
+ keyName: KeyName;
+ onPressIn: (key: KeyName) => void;
+ onPressOut: (key: KeyName) => void;
+}
+
+interface PlayingNote {
+ source: AudioBufferSourceNode;
+ envelope: GainNode;
+ startedAt: number;
+}
+
+const Keys = ['A', 'B', 'C', 'D', 'E'] as const;
+
+const sourceList: Partial> = {
+ A: '/react-native-audio-api/audio/sounds/C4.mp3',
+ C: '/react-native-audio-api/audio/sounds/Ds4.mp3',
+ E: '/react-native-audio-api/audio/sounds/Fs4.mp3',
+};
+
+const noteToFrequency = {
+ A: 261.626,
+ B: 277.193,
+ C: 311.127,
+ D: 329.628,
+ E: 369.994,
+};
+
+function getClosest(key: KeyName) {
+ let closestKey = 'A';
+ let minDiff = noteToFrequency.A - noteToFrequency[key];
+
+ for (const sourcedKey of Object.keys(sourceList)) {
+ const diff = noteToFrequency[sourcedKey] - noteToFrequency[key];
+
+ if (Math.abs(diff) < Math.abs(minDiff)) {
+ minDiff = diff;
+ closestKey = sourcedKey;
+ }
+ }
+
+ return closestKey;
+}
+
+const Button = ({ onPressIn, onPressOut, keyName }: ButtonProps) => (
+ onPressIn(keyName)}
+ onPressOut={() => onPressOut(keyName)}
+ style={({ pressed }) => ({
+ margin: 4,
+ padding: 12,
+ borderRadius: 2,
+ backgroundColor: pressed ? '#d2e6ff' : '#abcdef',
+ })}
+ >
+ {`${keyName}`}
+
+);
+
+const SimplePiano: FC = () => {
+ const audioContextRef = useRef(null);
+ const bufferListRef = useRef>({});
+ const playingNotesRef = useRef>({});
+
+ const onKeyPressIn = (which: KeyName) => {
+ let buffer = bufferListRef.current[which];
+ const aCtx = audioContextRef.current;
+ let playbackRate = 1;
+
+ if (!aCtx) {
+ return;
+ }
+
+ if (!buffer) {
+ const closestKey = getClosest(which);
+ buffer = bufferListRef.current[closestKey];
+ playbackRate = noteToFrequency[which] / noteToFrequency[closestKey];
+ }
+
+ const source = aCtx.createBufferSource();
+ const envelope = aCtx.createGain();
+ source.buffer = buffer;
+ source.playbackRate.value = playbackRate;
+
+ const tNow = aCtx.currentTime;
+
+ envelope.gain.setValueAtTime(0.001, tNow);
+ envelope.gain.exponentialRampToValueAtTime(1, tNow + 0.01);
+
+ source.connect(envelope);
+ envelope.connect(aCtx.destination);
+
+ source.start(tNow);
+ playingNotesRef.current[which] = { source, envelope, startedAt: tNow };
+ };
+
+ const onKeyPressOut = (which: KeyName) => {
+ const { source, envelope, startedAt } = playingNotesRef.current[which];
+
+ const aCtx = audioContextRef.current;
+
+ if (!source || !envelope || !aCtx) {
+ return;
+ }
+
+ const tNow = Math.max(aCtx.currentTime, startedAt + 1);
+
+ envelope.gain.exponentialRampToValueAtTime(0.0001, tNow + 0.08);
+ envelope.gain.setValueAtTime(0, tNow + 0.09);
+ source.stop(tNow + 0.1);
+
+ playingNotesRef.current[which] = undefined;
+ };
+
+ useEffect(() => {
+ if (!audioContextRef.current) {
+ audioContextRef.current = new AudioContext();
+ }
+
+ Object.entries(sourceList).forEach(async ([key, url]) => {
+ bufferListRef.current[key as KeyName] = await FileSystem.downloadAsync(
+ url,
+ `${FileSystem.documentDirectory}/${key}.mp3`
+ ).then(({ uri }) => audioContextRef.current!.decodeAudioDataSource(uri));
+ });
+
+ return () => {
+ audioContextRef.current?.close();
+ };
+ }, []);
+
+ return (
+
+ {Keys.map((key) => (
+
+ ))}
+
+ );
+};
+
+export default SimplePiano;
diff --git a/packages/audiodocs/static/audio/sounds/C4.mp3 b/packages/audiodocs/static/audio/sounds/C4.mp3
new file mode 100644
index 00000000..d5f5819d
Binary files /dev/null and b/packages/audiodocs/static/audio/sounds/C4.mp3 differ
diff --git a/packages/audiodocs/static/audio/sounds/Ds4.mp3 b/packages/audiodocs/static/audio/sounds/Ds4.mp3
new file mode 100644
index 00000000..f1312333
Binary files /dev/null and b/packages/audiodocs/static/audio/sounds/Ds4.mp3 differ
diff --git a/packages/audiodocs/static/audio/sounds/Fs4.mp3 b/packages/audiodocs/static/audio/sounds/Fs4.mp3
new file mode 100644
index 00000000..780ba594
Binary files /dev/null and b/packages/audiodocs/static/audio/sounds/Fs4.mp3 differ
diff --git a/packages/audiodocs/static/img/ADSR.svg b/packages/audiodocs/static/img/ADSR.svg
new file mode 100644
index 00000000..14379e59
--- /dev/null
+++ b/packages/audiodocs/static/img/ADSR.svg
@@ -0,0 +1,432 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ style="overflow:visible">
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+ Abdull
+
+
+ ADSR envelope
+
+
+
+
+
+
+
+
+
+ Amplitude
+
+
+
+
+ 0
+ t
+
+ Amp
+ max
+
+
+
+ key pressed
+
+
+ A
+
+ D
+
+
+ S
+
+ R
+
+
+ key released
+
+
+
+
diff --git a/packages/audiodocs/static/img/frequencies-on-piano.jpg b/packages/audiodocs/static/img/frequencies-on-piano.jpg
new file mode 100644
index 00000000..58a8415e
Binary files /dev/null and b/packages/audiodocs/static/img/frequencies-on-piano.jpg differ
diff --git a/packages/audiodocs/yarn.lock b/packages/audiodocs/yarn.lock
index da289337..02aba552 100644
--- a/packages/audiodocs/yarn.lock
+++ b/packages/audiodocs/yarn.lock
@@ -1422,6 +1422,11 @@
"@babel/helper-string-parser" "^7.25.9"
"@babel/helper-validator-identifier" "^7.25.9"
+"@braintree/sanitize-url@^6.0.0":
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz#923ca57e173c6b232bbbb07347b1be982f03e783"
+ integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==
+
"@colors/colors@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
@@ -1774,6 +1779,20 @@
use-sync-external-store "^1.2.0"
utility-types "^3.10.0"
+"@docusaurus/theme-mermaid@^2.4.3":
+ version "2.4.3"
+ resolved "https://registry.yarnpkg.com/@docusaurus/theme-mermaid/-/theme-mermaid-2.4.3.tgz#b40194fb4f46813a18d1350a188d43b68a8192dd"
+ integrity sha512-S1tZ3xpowtFiTrpTKmvVbRHUYGOlEG5CnPzWlO4huJT1sAwLR+pD6f9DYUlPv2+9NezF3EfUrUyW9xLH0UP58w==
+ dependencies:
+ "@docusaurus/core" "2.4.3"
+ "@docusaurus/module-type-aliases" "2.4.3"
+ "@docusaurus/theme-common" "2.4.3"
+ "@docusaurus/types" "2.4.3"
+ "@docusaurus/utils-validation" "2.4.3"
+ "@mdx-js/react" "^1.6.22"
+ mermaid "^9.2.2"
+ tslib "^2.4.0"
+
"@docusaurus/theme-search-algolia@2.4.3", "@docusaurus/theme-search-algolia@^2.4.3":
version "2.4.3"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.3.tgz#32d4cbefc3deba4112068fbdb0bde11ac51ece53"
@@ -4876,6 +4895,11 @@ command-exists@^1.2.8:
resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69"
integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==
+commander@7, commander@^7.2.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+ integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
commander@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
@@ -4891,11 +4915,6 @@ commander@^5.1.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
-commander@^7.2.0:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
- integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
-
commander@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
@@ -5064,6 +5083,20 @@ core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
+cose-base@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a"
+ integrity sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==
+ dependencies:
+ layout-base "^1.0.0"
+
+cose-base@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-2.2.0.tgz#1c395c35b6e10bb83f9769ca8b817d614add5c01"
+ integrity sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==
+ dependencies:
+ layout-base "^2.0.0"
+
cosmiconfig@^5.0.5, cosmiconfig@^5.1.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
@@ -5326,7 +5359,278 @@ csstype@^3.0.2, csstype@^3.1.3:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
-dayjs@^1.8.15:
+cytoscape-cose-bilkent@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz#762fa121df9930ffeb51a495d87917c570ac209b"
+ integrity sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==
+ dependencies:
+ cose-base "^1.0.0"
+
+cytoscape-fcose@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz#e4d6f6490df4fab58ae9cea9e5c3ab8d7472f471"
+ integrity sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==
+ dependencies:
+ cose-base "^2.2.0"
+
+cytoscape@^3.23.0:
+ version "3.30.4"
+ resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.30.4.tgz#3404da0a159c00a1a3df2c85b2b43fdc66a0e28e"
+ integrity sha512-OxtlZwQl1WbwMmLiyPSEBuzeTIQnwZhJYYWFzZ2PhEHVFwpeaqNIkUzSiso00D98qk60l8Gwon2RP304d3BJ1A==
+
+"d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5"
+ integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
+ dependencies:
+ internmap "1 - 2"
+
+d3-axis@3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322"
+ integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==
+
+d3-brush@3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c"
+ integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-drag "2 - 3"
+ d3-interpolate "1 - 3"
+ d3-selection "3"
+ d3-transition "3"
+
+d3-chord@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966"
+ integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==
+ dependencies:
+ d3-path "1 - 3"
+
+"d3-color@1 - 3", d3-color@3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
+ integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
+
+d3-contour@4:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-4.0.2.tgz#bb92063bc8c5663acb2422f99c73cbb6c6ae3bcc"
+ integrity sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==
+ dependencies:
+ d3-array "^3.2.0"
+
+d3-delaunay@6:
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz#98169038733a0a5babbeda55054f795bb9e4a58b"
+ integrity sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==
+ dependencies:
+ delaunator "5"
+
+"d3-dispatch@1 - 3", d3-dispatch@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
+ integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
+
+"d3-drag@2 - 3", d3-drag@3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
+ integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-selection "3"
+
+"d3-dsv@1 - 3", d3-dsv@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73"
+ integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==
+ dependencies:
+ commander "7"
+ iconv-lite "0.6"
+ rw "1"
+
+"d3-ease@1 - 3", d3-ease@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
+ integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
+
+d3-fetch@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22"
+ integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==
+ dependencies:
+ d3-dsv "1 - 3"
+
+d3-force@3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4"
+ integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-quadtree "1 - 3"
+ d3-timer "1 - 3"
+
+"d3-format@1 - 3", d3-format@3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641"
+ integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==
+
+d3-geo@3:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.1.1.tgz#6027cf51246f9b2ebd64f99e01dc7c3364033a4d"
+ integrity sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==
+ dependencies:
+ d3-array "2.5.0 - 3"
+
+d3-hierarchy@3:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6"
+ integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==
+
+"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
+ integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
+ dependencies:
+ d3-color "1 - 3"
+
+"d3-path@1 - 3", d3-path@3, d3-path@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526"
+ integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==
+
+d3-polygon@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398"
+ integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==
+
+"d3-quadtree@1 - 3", d3-quadtree@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f"
+ integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==
+
+d3-random@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4"
+ integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==
+
+d3-scale-chromatic@3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#34c39da298b23c20e02f1a4b239bd0f22e7f1314"
+ integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==
+ dependencies:
+ d3-color "1 - 3"
+ d3-interpolate "1 - 3"
+
+d3-scale@4:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396"
+ integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
+ dependencies:
+ d3-array "2.10.0 - 3"
+ d3-format "1 - 3"
+ d3-interpolate "1.2.0 - 3"
+ d3-time "2.1.1 - 3"
+ d3-time-format "2 - 4"
+
+"d3-selection@2 - 3", d3-selection@3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
+ integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
+
+d3-shape@3:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5"
+ integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==
+ dependencies:
+ d3-path "^3.1.0"
+
+"d3-time-format@2 - 4", d3-time-format@4:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a"
+ integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==
+ dependencies:
+ d3-time "1 - 3"
+
+"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7"
+ integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==
+ dependencies:
+ d3-array "2 - 3"
+
+"d3-timer@1 - 3", d3-timer@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
+ integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
+
+"d3-transition@2 - 3", d3-transition@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
+ integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
+ dependencies:
+ d3-color "1 - 3"
+ d3-dispatch "1 - 3"
+ d3-ease "1 - 3"
+ d3-interpolate "1 - 3"
+ d3-timer "1 - 3"
+
+d3-zoom@3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
+ integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-drag "2 - 3"
+ d3-interpolate "1 - 3"
+ d3-selection "2 - 3"
+ d3-transition "2 - 3"
+
+d3@^7.4.0, d3@^7.8.2:
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/d3/-/d3-7.9.0.tgz#579e7acb3d749caf8860bd1741ae8d371070cd5d"
+ integrity sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==
+ dependencies:
+ d3-array "3"
+ d3-axis "3"
+ d3-brush "3"
+ d3-chord "3"
+ d3-color "3"
+ d3-contour "4"
+ d3-delaunay "6"
+ d3-dispatch "3"
+ d3-drag "3"
+ d3-dsv "3"
+ d3-ease "3"
+ d3-fetch "3"
+ d3-force "3"
+ d3-format "3"
+ d3-geo "3"
+ d3-hierarchy "3"
+ d3-interpolate "3"
+ d3-path "3"
+ d3-polygon "3"
+ d3-quadtree "3"
+ d3-random "3"
+ d3-scale "4"
+ d3-scale-chromatic "3"
+ d3-selection "3"
+ d3-shape "3"
+ d3-time "3"
+ d3-time-format "4"
+ d3-timer "3"
+ d3-transition "3"
+ d3-zoom "3"
+
+dagre-d3-es@7.0.9:
+ version "7.0.9"
+ resolved "https://registry.yarnpkg.com/dagre-d3-es/-/dagre-d3-es-7.0.9.tgz#aca12fccd9d09955a4430029ba72ee6934542a8d"
+ integrity sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w==
+ dependencies:
+ d3 "^7.8.2"
+ lodash-es "^4.17.21"
+
+dayjs@^1.11.7, dayjs@^1.8.15:
version "1.11.13"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
@@ -5440,6 +5744,13 @@ del@^6.1.1:
rimraf "^3.0.2"
slash "^3.0.0"
+delaunator@5:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.1.tgz#39032b08053923e924d6094fe2cde1a99cc51278"
+ integrity sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==
+ dependencies:
+ robust-predicates "^3.0.2"
+
denodeify@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631"
@@ -5578,6 +5889,11 @@ domhandler@^5.0.2, domhandler@^5.0.3:
dependencies:
domelementtype "^2.3.0"
+dompurify@2.4.3:
+ version "2.4.3"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.3.tgz#f4133af0e6a50297fc8874e2eaedc13a3c308c03"
+ integrity sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==
+
domutils@^2.5.2, domutils@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
@@ -5645,6 +5961,11 @@ electron-to-chromium@^1.5.73:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.73.tgz#f32956ce40947fa3c8606726a96cd8fb5bb5f720"
integrity sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==
+elkjs@^0.8.2:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.8.2.tgz#c37763c5a3e24e042e318455e0147c912a7c248e"
+ integrity sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==
+
emoji-regex@^10.2.1:
version "10.4.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4"
@@ -6912,7 +7233,7 @@ iconv-lite@0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
-iconv-lite@0.6.3, iconv-lite@^0.6.3:
+iconv-lite@0.6, iconv-lite@0.6.3, iconv-lite@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
@@ -7046,6 +7367,11 @@ inline-style-prefixer@^6.0.1:
css-in-js-utils "^3.1.0"
fast-loops "^1.1.3"
+"internmap@1 - 2":
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009"
+ integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==
+
interpret@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
@@ -7596,6 +7922,11 @@ keyv@^3.0.0:
dependencies:
json-buffer "3.0.0"
+khroma@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/khroma/-/khroma-2.1.0.tgz#45f2ce94ce231a437cf5b63c2e886e6eb42bbbb1"
+ integrity sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==
+
kind-of@^6.0.0, kind-of@^6.0.2:
version "6.0.3"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
@@ -7626,6 +7957,16 @@ launch-editor@^2.6.0:
picocolors "^1.0.0"
shell-quote "^1.8.1"
+layout-base@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/layout-base/-/layout-base-1.0.2.tgz#1291e296883c322a9dd4c5dd82063721b53e26e2"
+ integrity sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==
+
+layout-base@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/layout-base/-/layout-base-2.0.1.tgz#d0337913586c90f9c2c075292069f5c2da5dd285"
+ integrity sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==
+
leven@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
@@ -7703,6 +8044,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
+lodash-es@^4.17.21:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
+ integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
+
lodash.curry@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170"
@@ -8012,6 +8358,28 @@ merge2@^1.3.0, merge2@^1.4.1:
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+mermaid@^9.2.2:
+ version "9.4.3"
+ resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-9.4.3.tgz#62cf210c246b74972ea98c19837519b6f03427f2"
+ integrity sha512-TLkQEtqhRSuEHSE34lh5bCa94KATCyluAXmFnNI2PRZwOpXFeqiJWwZl+d2CcemE1RS6QbbueSSq9QIg8Uxcyw==
+ dependencies:
+ "@braintree/sanitize-url" "^6.0.0"
+ cytoscape "^3.23.0"
+ cytoscape-cose-bilkent "^4.1.0"
+ cytoscape-fcose "^2.1.0"
+ d3 "^7.4.0"
+ dagre-d3-es "7.0.9"
+ dayjs "^1.11.7"
+ dompurify "2.4.3"
+ elkjs "^0.8.2"
+ khroma "^2.0.0"
+ lodash-es "^4.17.21"
+ non-layered-tidy-tree-layout "^2.0.2"
+ stylis "^4.1.2"
+ ts-dedent "^2.2.0"
+ uuid "^9.0.0"
+ web-worker "^1.2.0"
+
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -8875,6 +9243,11 @@ node-stream-zip@^1.9.1:
resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea"
integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==
+non-layered-tidy-tree-layout@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz#57d35d13c356643fc296a55fb11ac15e74da7804"
+ integrity sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==
+
nopt@^7.0.0:
version "7.2.1"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7"
@@ -10601,6 +10974,11 @@ rimraf@~2.6.2:
dependencies:
glob "^7.1.3"
+robust-predicates@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771"
+ integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==
+
rtl-detect@^1.0.4:
version "1.1.2"
resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.1.2.tgz#ca7f0330af5c6bb626c15675c642ba85ad6273c6"
@@ -10623,6 +11001,11 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
+rw@1:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
+ integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==
+
rxjs@^7.5.4:
version "7.8.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
@@ -11291,6 +11674,11 @@ stylis@4.2.0:
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51"
integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==
+stylis@^4.1.2:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4"
+ integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==
+
sudo-prompt@^9.0.0:
version "9.2.1"
resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd"
@@ -11505,6 +11893,11 @@ ts-api-utils@^1.3.0:
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064"
integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==
+ts-dedent@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5"
+ integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==
+
tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.1, tslib@^2.6.2:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
@@ -11934,6 +12327,11 @@ uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+uuid@^9.0.0:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
+ integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
+
uvu@^0.5.0, uvu@^0.5.6:
version "0.5.6"
resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df"
@@ -12080,6 +12478,11 @@ web-namespaces@^1.0.0:
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"
integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==
+web-worker@^1.2.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.3.0.tgz#e5f2df5c7fe356755a5fb8f8410d4312627e6776"
+ integrity sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==
+
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"