Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@remotion/media-utils: Add visualizeAudioWaveform() and createSmoothSvgPath() APIs #578

Merged
merged 34 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1b7169f
add `visualizeAudioWaveform` api
Iamshankhadeep Aug 28, 2021
83d47de
visualizeAudioWaveform api to support waveformDuration
Iamshankhadeep Sep 5, 2021
b6f8b04
added waveform example
Iamshankhadeep Sep 5, 2021
f5cb2e4
update push.yml
Iamshankhadeep Sep 5, 2021
5834aec
voice
JonnyBurger Sep 6, 2021
e418f4f
update demo
JonnyBurger Sep 6, 2021
b611f87
Merge branch 'main' into audio-waveform
Iamshankhadeep Oct 30, 2021
1acf47a
update docs of `visualizeAudioWaveform()`
Iamshankhadeep Nov 20, 2021
410f9ed
change push.yaml file
Iamshankhadeep Nov 20, 2021
be78542
Merge branch 'main' into audio-waveform
Iamshankhadeep Nov 20, 2021
81ceeeb
smoothen + docs
JonnyBurger Dec 4, 2021
5ade5ed
Merge branch 'main' into audio-waveform
JonnyBurger Jan 10, 2022
046e45a
Merge branch 'main' into audio-waveform
JonnyBurger May 27, 2022
74962a5
Update Video.tsx
JonnyBurger May 27, 2022
959b539
Update package.json
JonnyBurger May 27, 2022
e672713
separate voice and music
JonnyBurger May 27, 2022
0cfd6eb
getting there
JonnyBurger May 27, 2022
944c606
Update index.ts
JonnyBurger May 27, 2022
d9ddded
looking good
JonnyBurger May 27, 2022
479673b
fix wrong timeline display
JonnyBurger May 27, 2022
75ddfce
two channel experimentation
JonnyBurger May 27, 2022
5dd7807
Update AudioWaveformExamples.tsx
JonnyBurger May 27, 2022
5114833
Merge branch 'main' into audio-waveform
JonnyBurger Jul 11, 2022
f66f43f
Merge branch 'main' into audio-waveform
JonnyBurger Feb 19, 2025
5fee64e
cool
JonnyBurger Feb 19, 2025
3bee2d9
Discard changes to packages/studio/src/components/AudioWaveform.tsx
JonnyBurger Feb 19, 2025
9f404f0
alright
JonnyBurger Feb 19, 2025
8d5d151
Merge branch 'audio-waveform' of https://github.com/remotion-dev/remo…
JonnyBurger Feb 19, 2025
df4d7cf
nice
JonnyBurger Feb 19, 2025
4883a6d
use podcast instead
JonnyBurger Feb 19, 2025
2c3b67d
good enough to ship
JonnyBurger Feb 19, 2025
ecb2200
Update create-smooth-svg-path.mdx
JonnyBurger Feb 19, 2025
37b258a
Update validate-channel.ts
JonnyBurger Feb 19, 2025
562d7c8
media utils
JonnyBurger Feb 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 190 additions & 0 deletions packages/docs/components/AudioWaveformExamples.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import {
createSmoothSvgPath,
useAudioData,
visualizeAudioWaveform,
} from '@remotion/media-utils';
import {Player} from '@remotion/player';
import React from 'react';
import {
AbsoluteFill,
Audio,
staticFile,
useCurrentFrame,
useVideoConfig,
} from 'remotion';

const src = staticFile('podcast.wav');

const BaseExample: React.FC = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const audioDataVoice = useAudioData(src);
const {width} = useVideoConfig();
const height = 300;

if (!audioDataVoice) {
return null;
}

const waveform = visualizeAudioWaveform({
fps,
frame,
audioData: audioDataVoice,
numberOfSamples: 32,
windowInSeconds: 1 / fps,
channel: 0,
});

const p = createSmoothSvgPath({
points: waveform.map((x, i) => {
return {
x: (i / (waveform.length - 1)) * width,
y: x * height * 2 + height / 2,
};
}),
});

return (
<div style={{flex: 1}}>
<Audio src={src} />
<AbsoluteFill style={{justifyContent: 'center', alignItems: 'center'}}>
<svg
style={{backgroundColor: '#0B84F3'}}
viewBox={`0 0 ${width} ${height}`}
width={width}
height={height}
>
<path stroke="white" fill="none" strokeWidth={10} d={p as string} />
</svg>
</AbsoluteFill>
</div>
);
};

const MovingExample: React.FC = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const audioDataVoice = useAudioData(src);
const {width} = useVideoConfig();
const height = 300;

if (!audioDataVoice) {
return null;
}

const waveform = visualizeAudioWaveform({
fps,
frame,
audioData: audioDataVoice,
numberOfSamples: 32,
windowInSeconds: 10 / fps,
channel: 0,
});

const p = createSmoothSvgPath({
points: waveform.map((x, i) => {
return {
x: (i / (waveform.length - 1)) * width,
y: x * height * 2 + height / 2,
};
}),
});

return (
<div style={{flex: 1}}>
<Audio src={src} />
<AbsoluteFill style={{justifyContent: 'center', alignItems: 'center'}}>
<svg
style={{backgroundColor: '#0B84F3'}}
viewBox={`0 0 ${width} ${height}`}
width={width}
height={height}
>
<path stroke="#ffffff" fill="none" strokeWidth={10} d={p as string} />
</svg>
</AbsoluteFill>
</div>
);
};

const PosterizedExample: React.FC = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const audioDataVoice = useAudioData(src);
const {width} = useVideoConfig();
const height = 300;

if (!audioDataVoice) {
return null;
}

const waveform = visualizeAudioWaveform({
fps,
frame: Math.round(frame / 3) * 3,
audioData: audioDataVoice,
numberOfSamples: 16,
windowInSeconds: 1 / fps,
channel: 0,
});

const p = createSmoothSvgPath({
points: waveform.map((x, i) => {
return {
x: (i / (waveform.length - 1)) * width,
y: x * height * 2 + height / 2,
};
}),
});

return (
<div style={{flex: 1}}>
<Audio src={src} />
<AbsoluteFill style={{justifyContent: 'center', alignItems: 'center'}}>
<svg
style={{backgroundColor: ' #0B84F3'}}
viewBox={`0 0 ${width} ${height}`}
width={width}
height={height}
>
<path stroke="white" fill="none" strokeWidth={10} d={p as string} />
</svg>
</AbsoluteFill>
</div>
);
};

export const AudioWaveFormExample: React.FC<{
readonly type: 'base' | 'moving' | 'posterized';
}> = ({type}) => {
const component = (() => {
if (type === 'base') {
return BaseExample;
}

if (type === 'moving') {
return MovingExample;
}

if (type === 'posterized') {
return PosterizedExample;
}

throw new TypeError('oops');
})();
return (
<div>
<Player
component={component}
compositionWidth={1080}
compositionHeight={300}
controls
durationInFrames={300}
fps={30}
style={{
width: '100%',
}}
loop
/>
</div>
);
};
10 changes: 9 additions & 1 deletion packages/docs/components/TableOfContents/media-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,15 @@ export const TableOfContents: React.FC = () => {
</TOCItem>
<TOCItem link="/docs/visualize-audio">
<strong>visualizeAudio()</strong>
<div>Processes a waveform for visualization</div>
<div>Process a music waveform for visualization</div>
</TOCItem>
<TOCItem link="/docs/media-utils/visualize-audio-waveform">
<strong>visualizeAudioWaveform()</strong>
<div>Process a voice waveform for visualization</div>
</TOCItem>
<TOCItem link="/docs/media-utils/create-smooth-svg-path">
<strong>createSmoothSvgPath()</strong>
<div>Turn waveform points into a smooth SVG path</div>
</TOCItem>
</Grid>
</div>
Expand Down
23 changes: 16 additions & 7 deletions packages/docs/docs/audio-visualization.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ Remotion has APIs for visualizing audio, for example for creating audiograms or

The `@remotion/media-utils` package provides helper functions for reading and processing audio. Using the [`getAudioData()`](/docs/get-audio-data) API you can read audio, and using the [`useAudioData()`](/docs/use-audio-data) helper hook you can load this audio data directly into your component.

Using the [`visualizeAudio()`](/docs/visualize-audio) API, you can get an audio spectrum for the current frame, with the `numberOfSamples` parameter being an option to control the amount of detail you need.
## Bar visualization

Refer to the documentation of the above mentioned functions to learn more.
Using the [`visualizeAudio()`](/docs/visualize-audio) API, you can get an audio spectrum for the current frame.

Bar visualizations are ideal for visualizing music.

```tsx twoslash
import {useAudioData, visualizeAudio} from '@remotion/media-utils';
Expand Down Expand Up @@ -41,21 +43,27 @@ export const MyComponent: React.FC = () => {
<div>
<Audio src={music} />
{visualization.map((v) => {
return (
<div style={{width: 1000 * v, height: 15, backgroundColor: 'blue'}} />
);
return <div style={{width: 1000 * v, height: 15, backgroundColor: 'blue'}} />;
})}
</div>
);
};
```

## Waveform visualization

See an example for a waveform visualizations using [`visualizeAudioWaveform()`](/docs/media-utils/visualize-audio-waveform) here.

import {AudioWaveFormExample} from '../components/AudioWaveformExamples.tsx';

<AudioWaveFormExample type="moving" />

## Working with large files

[`useAudioData()`](/docs/use-audio-data) loads the entire audio file into memory.
[`useAudioData()`](/docs/use-audio-data) loads the entire audio file into memory.
This is fine for small files, but for large files, it can be slow and consume a lot of memory.

Use [`useWindowedAudioData()`](/docs/use-windowed-audio-data) to only load a portion of the audio around the current frame.
Use [`useWindowedAudioData()`](/docs/use-windowed-audio-data) to only load a portion of the audio around the current frame.
The tradeoff is that this API only works with `.wav` files.

## See also
Expand All @@ -64,3 +72,4 @@ The tradeoff is that this API only works with `.wav` files.
- [`useAudioData()`](/docs/use-audio-data)
- [`useWindowedAudioData()`](/docs/use-windowed-audio-data)
- [`visualizeAudio()`](/docs/visualize-audio)
- [`visualizeAudioWaveform()`](/docs/media-utils/visualize-audio-waveform)
12 changes: 12 additions & 0 deletions packages/docs/docs/get-waveform-portion.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ _number_

How big you want the result array to be. The function will compress the waveform to fit in `numberOfSamples` data points.

### `channel`

_number_

Which channel to use. Defaults to 0.

### `outputRange`

_number_

The range of the output values. Either `minus-one-to-one` or `zero-to-one`. Defaults to `zero-to-one`.

## Return value

`Bar[]` - An array of objects with the following properties:
Expand Down
32 changes: 32 additions & 0 deletions packages/docs/docs/media-utils/create-smooth-svg-path.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: createSmoothSvgPath()
id: create-smooth-svg-path
crumb: '@remotion/media-utils'
---

_Part of the `@remotion/media-utils`_ package of helper functions.

This function takes points, usually generated from [`visualizeAudioWaveform()`](/docs/media-utils/visualize-audio-waveform) or [`visualizeAudio()`](/docs/visualize-audio), and adds SVG `C` (curve) statements inbetween them to create a smooth path.

## Example

```tsx twoslash
import {createSmoothSvgPath} from '@remotion/media-utils';
import React from 'react';

const points = [
{x: 0, y: 0},
{x: 100, y: 100},
{x: 200, y: 50},
{x: 300, y: 150},
];

const path = createSmoothSvgPath({points});
```

See a practical example [here](http://localhost:3001/docs/media-utils/visualize-audio-waveform#examples).

## See also

- [Source code for this function](https://github.com/remotion-dev/remotion/blob/main/packages/media-utils/src/create-smooth-svg-path.ts)
- [Audio visualization](/docs/audio-visualization)
Loading
Loading