Skip to content

Commit

Permalink
Add Features: Proximity Mode & Add/Delete Shape Points (#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
ejarzo authored Dec 24, 2022
1 parent 9db5281 commit e61f922
Show file tree
Hide file tree
Showing 32 changed files with 675 additions and 1,150 deletions.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v12.13.0
12
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,32 @@ Using this app, one can:

Sound is created by drawing polygons on the canvas and clicking PLAY (or space bar). Each polygon represents a looping musical phrase.

There are two modes: Draw and Edit (toggle between them with Tab). While in DRAW mode, clicking on the canvas allows you to draw polygons by placing vertices. In EDIT mode, you can adjust each polygon by dragging its vertices, or by dragging the entire polygon to a new position. Shapes higher up on the canvas start at higher notes than shapes lower down. Moving shapes left or right places them in stereo space (left/right on your speakers or headphones). Also in EDIT mode, you can click on a shape to display a context menu with more options.
There are two modes: Draw and Edit (toggle between them with Tab). While in DRAW mode, clicking on the canvas allows you to draw polygons by placing vertices. In EDIT mode, You can **add** vertices by clicking on an edge midpoint, or **delete** vertices by double-clicking them. You can also drag each polygon to adjust its position, or drag the entire shape to a new position. Shapes higher up on the canvas start at higher notes than shapes lower down. Moving shapes left or right places them in stereo space (left/right on your speakers or headphones). Also in EDIT mode, you can click on a shape to display a context menu with more options.

Each shape is a certain color. The current color with which you are drawing is controlled with the color palette in the toolbar. A shape's color determines which instrument it uses to produce sound. The sounds for each color can be controlled with the colored panels at the bottom of the screen; if the red panel is set to the "Cello" instrument, every red shape will make a cello sound.

When a shape plays, a node traverses the perimeter of the shape at a constant speed, sounding a note at each vertex. The first note of a shape is determined by the shape's `y` position on the plane. The note for each subsequent edge is determined by the angle between that edge and the previous edge. This angle determines the musical interval between the two notes(edges). For example: A sharp right turn means that the next note is much higher than the previous, while a shallow left turn means that the next note is a little lower. When the last point is reached, the loop starts again.

### Toolbar

![Screen Shot 2021-02-12 at 4 13 20 PM](https://user-images.githubusercontent.com/9386882/107823119-3b50ea00-6d4d-11eb-9e46-38dbeb813318.png)
![Toolbar screenshot](assets/readme-images/toolbar.png)

The toolbar allows you to adjust various aspects of your project.

| Name | Description |
| --------- | ----------- |
| Play/Stop | Pressing play starts all shapes at their origin point. Shapes that are added during playback will start playing as soon as they are completed. |
| Record | Pressing record allows you to download your project as an audio file (.wav). If playback is stopped when you click record, recording will begin when you begin playback. If the project is playing when you click record, the recording will start instantly. Pressing stop or record again will end the recording and show a window where you can listen and download the file that was generated. |
| Color | Select the color of the shapes you are drawing. Different colored shapes produce different sounds. |
| Draw Tool | Draw mode allows you to create shapes. Click to place vertices. Click on the origin point to complete a shape. Right click to cancel. |
| Edit Tool  | Edit mode allows you to adjust your shapes. Drag vertices to edit the perimeter of your shape. Drag the whole shape to move it. Click on a shape to show more detailed options (see shape controls). |
| Grid | When selected, the grid is shown and all points will snap to the grid when drawn, or when shapes are moved |
| Sync | When selected, shapes will snap and lock to the same length - so that they will loop at the same time. Shapes can be “halved’ or “doubled” so that they loop half or twice as often. This allows for a defined rhythm. |
| Tempo | Change the speed of playback |
| Key | Select the root note |
| Scale | Select the musical scale (mode) |
| Clear | Delete all shapes |
| Name | Description |
|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Play/Stop | Pressing play starts all shapes at their origin point. Shapes that are added during playback will start playing as soon as they are completed. |
| Record | Pressing record allows you to download your project as an audio file (.wav). If playback is stopped when you click record, recording will begin when you begin playback. If the project is playing when you click record, the recording will start instantly. Pressing stop or record again will end the recording and show a window where you can listen and download the file that was generated. |
| Color | Select the color of the shapes you are drawing. Different colored shapes produce different sounds. |
| Draw Tool | Draw mode allows you to create shapes. Click to place vertices. Click on the origin point to complete a shape. Right click to cancel. |
| Edit Tool  | Edit mode allows you to adjust your shapes. Drag vertices to edit the perimeter of your shape. Drag the whole shape to move it. Click on a shape to show more detailed options (see shape controls). |
| Grid | When selected, the grid is shown and all points will snap to the grid when drawn, or when shapes are moved |
| Sync | When selected, shapes will snap and lock to the same length - so that they will loop at the same time. Shapes can be “halved’ or “doubled” so that they loop half or twice as often. This allows for a defined rhythm. |
| Proximity Mode | Turn on Proximity Mode to experience your project spatially, where the listening position is controlled with the mouse. Hover over the dropdown icon to adjust the listening radius. |
| Tempo | Change the speed of playback |
| Key | Select the root note |
| Scale | Select the musical scale (mode) |
| Clear | Delete all shapes |

### Exporting

Expand All @@ -67,7 +68,7 @@ There are two ways to export your project: as audio or as MIDI.

![image](https://user-images.githubusercontent.com/9386882/107823235-73f0c380-6d4d-11eb-94c8-5c8ce5312639.png)

The Shape menu allows you to control a shape's properties. CLICK on a shape to display this menu.
The Shape menu allows you to control a shape's properties. CLICK on a shape (in EDIT mode) to display this menu.

| Control | Description |
| -------------- | ------------------------------- |
Expand Down
Binary file modified assets/readme-images/toolbar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions functions/utils/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const PROJECT_FRAGMENT = `
tonic
isSnapToGridActive
isAutoQuantizeActive
isProximityModeActive
proximityModeRadius
isGridActive
dateCreated
selectedSynths
Expand Down
6 changes: 6 additions & 0 deletions functions/utils/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export const typeDefs = gql`
scale: String!
isSnapToGridActive: Boolean!
isAutoQuantizeActive: Boolean!
isProximityModeActive: Boolean
proximityModeRadius: Int
tonic: String!
isGridActive: Boolean!
shapesList: [Shape!]
Expand All @@ -56,6 +58,8 @@ export const typeDefs = gql`
isGridActive: Boolean!
isSnapToGridActive: Boolean!
isAutoQuantizeActive: Boolean!
isProximityModeActive: Boolean!
proximityModeRadius: Int!
shapesList: [ShapeInput!]
selectedSynths: [Synth!]
dateCreated: Long
Expand All @@ -70,6 +74,8 @@ export const typeDefs = gql`
isGridActive: Boolean
isSnapToGridActive: Boolean
isAutoQuantizeActive: Boolean
isProximityModeActive: Boolean!
proximityModeRadius: Int!
shapesList: [ShapeInput!]
selectedSynths: [Synth!]
dateCreated: Long
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"react-scripts": "^3.1.1",
"react-select": "^3.0.8",
"teoria": "^2.5.0",
"tone": "^13.8.12",
"tone": "13.8.25",
"utf-8-validate": "^5.0.2"
},
"scripts": {
Expand Down
2 changes: 2 additions & 0 deletions schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ type Project {
isGridActive: Boolean!
isSnapToGridActive: Boolean!
isAutoQuantizeActive: Boolean!
isProximityModeActive: Boolean
proximityModeRadius: Int
shapesList: [Shape!]
userId: String!
userName: String!
Expand Down
4 changes: 2 additions & 2 deletions src/components/CheckboxButton/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function CheckboxButton(props) {
backgroundColor: props.checked ? grayLightest : getDarker(props.color),
color: props.checked ? props.color : grayLightest,
}
: defaultStyle;
: { ...defaultStyle, ...props.labelStyle };

return (
<div className={styles.inputWrapper}>
Expand All @@ -42,7 +42,7 @@ function CheckboxButton(props) {
htmlFor={props.label.toLowerCase()}
style={labelStyle}
>
{props.label}
{props.renderLabel ? props.renderLabel(props.label) : props.label}
</label>
</div>
);
Expand Down
3 changes: 2 additions & 1 deletion src/components/CheckboxButton/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
}

.checkboxLabel {
user-select: none;
display: grid;
align-items: center;
height: 100%;
Expand All @@ -18,6 +19,6 @@
padding: 0 7px;
text-align: center;
font-size: 1em;
line-height: 2em;
line-height: 1;
border: 1px solid transparent;
}
8 changes: 7 additions & 1 deletion src/components/Downloads/Component.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from 'antd';
import { Alert, Button } from 'antd';
import { DownloadOutlined } from '@ant-design/icons';
import styles from './styles.module.css';

Expand All @@ -26,6 +26,12 @@ function Downloads(props) {
</span>
)}
</div>
<Alert
style={{ marginBottom: 20 }}
showIcon
type="warning"
message="Recordings will be lost when you leave or reload the page."
/>
<ul>
{downloadUrls
.sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1))
Expand Down
34 changes: 34 additions & 0 deletions src/components/HeaderMenu/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useColorThemeContext } from 'context/ColorThemeContext/useColorThemeCon
import { appColors, THEMES } from 'utils/color';
import Color from 'color';
import AboutModalContent from 'components/AboutModalContent';
import WhatsNewModalContent from 'components/WhatsNewModalContent';

const { Link: AntLink } = Typography;

Expand All @@ -34,6 +35,7 @@ function HeaderMenu(props) {
// TODO: reveal when dark mode works
const showDarkModeButton = false;
const [isAboutModalVisible, setIsAboutModalVisible] = useState(false);
const [isWhatsNewVisible, setIsWhatsNewVisible] = useState(false);

return (
<CurrentUserContextConsumer>
Expand Down Expand Up @@ -62,6 +64,15 @@ function HeaderMenu(props) {
>
<AboutModalContent />
</Modal>
<Modal
// title="About Shape Your Music"
visible={isWhatsNewVisible}
onCancel={() => setIsWhatsNewVisible(false)}
footer={null}
// width={700}
>
<WhatsNewModalContent />
</Modal>
<div className={styles.headerMenuLinks}>
<HeaderLink exact to={ROUTES.INDEX}>
Create
Expand Down Expand Up @@ -99,6 +110,29 @@ function HeaderMenu(props) {
>
GitHub
</a>
<div
style={{
borderRadius: '50%',
width: 4,
height: 4,
background: '#555',
position: 'relative',
top: 8,
marginRight: 10,
}}
></div>
<AntLink
style={{
background: 'pink',
padding: '0 8px',
borderRadius: 2,
}}
size="small"
type="link"
onClick={() => setIsWhatsNewVisible(true)}
>
{`What's New`}
</AntLink>
</div>
<div className={styles.headerAccount}>
<div>
Expand Down
1 change: 1 addition & 0 deletions src/components/Knob/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

.knobTitle {
color: #fff;
user-select: none;
text-transform: capitalize;
}

Expand Down
26 changes: 22 additions & 4 deletions src/components/Project/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ import { getDefaultParamValues } from 'utils/synths';
import styles from './styles.module.css';
import { useRecorder } from './useRecorder';
import { useAudioOutput } from './useAudioOutput';
import { PROJECT_ACTIONS, TOOL_TYPES, getInitState } from 'utils/project';
import {
PROJECT_ACTIONS,
TOOL_TYPES,
getInitState,
MIN_PROXIMITY_RADIUS,
MAX_PROXIMITY_RADIUS,
} from 'utils/project';
import useUnload from 'hooks/useUnload';

export default props => {
Expand Down Expand Up @@ -85,11 +91,24 @@ export default props => {
...state,
isAutoQuantizeActive: !state.isAutoQuantizeActive,
};
case PROJECT_ACTIONS.SET_TEMPO:
case PROJECT_ACTIONS.TOGGLE_PROXIMITY_MODE:
return {
...state,
isProximityModeActive: !state.isProximityModeActive,
};
case PROJECT_ACTIONS.SET_PROXIMITY_MODE_RADIUS: {
const proximityModeRadius = Math.max(
Math.min(action.payload, MAX_PROXIMITY_RADIUS),
MIN_PROXIMITY_RADIUS
);
return { ...state, proximityModeRadius };
}
case PROJECT_ACTIONS.SET_TEMPO: {
const min = 1;
const max = 100;
const tempo = Math.max(Math.min(action.payload, max), min);
return { ...state, tempo };
}
case PROJECT_ACTIONS.SET_TONIC:
return {
...state,
Expand Down Expand Up @@ -160,8 +179,7 @@ export default props => {
currentState
);

// prevent unload if user can save, there are shapes drawn, and the current state is different than the initial state

// prevent unload if user can save, there are shapes drawn, and the current state is different from the initial state
if (showSaveButton && hasShapes && !statesAreEqual) {
e.preventDefault();
e.returnValue = '';
Expand Down
Loading

0 comments on commit e61f922

Please sign in to comment.