Skip to content
Draft
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
86d29e7
feat: recording to file - ios dirty
michalsek Oct 20, 2025
a673542
fix: ts & eslint
michalsek Oct 21, 2025
f183220
fix: unnecessary dependency?
michalsek Oct 21, 2025
8f8a80f
fix: cleaner ts audio recorder interface
michalsek Oct 21, 2025
5a51ecc
feat: installer and host object
michalsek Oct 21, 2025
a2ff6a1
feat: ios file writter
michalsek Oct 22, 2025
4f7c7cc
feat: ios file writter
michalsek Oct 22, 2025
d20aa87
feat: avaudiorecorder drives me crazy
michalsek Oct 22, 2025
0836570
fix: cleanup
michalsek Oct 22, 2025
b30fccf
fix: deconstructor of IOSFileWriter
michalsek Oct 23, 2025
c1cfdbb
feat: rebuild ffmpeg with encoders, basic structure for new android r…
michalsek Oct 23, 2025
0a26842
feat: android miniaudio encoder
michalsek Oct 24, 2025
106d119
refactor android file backends
michalsek Oct 24, 2025
e227cbc
feat: android file backends clean implementation + fail safety
michalsek Oct 25, 2025
f656540
fix: cleanup miniaudio impl
michalsek Oct 26, 2025
825986c
feat: dirty ffmpeg writer
michalsek Oct 26, 2025
a6a5960
chore: bump version to 0.11.0-alpha.0
michalsek Oct 27, 2025
2da984e
feat: ios callback, pause, resume
michalsek Oct 29, 2025
f11dc51
feat: both - duration info, android - file size, recorded duration
michalsek Oct 29, 2025
0cac7d1
feat: android recorder callback class
michalsek Oct 30, 2025
285e566
chore: alpha 1
michalsek Oct 30, 2025
21a001a
Merge branch 'main' into feat/audio-recorder-to-file
michalsek Nov 6, 2025
f3f3c99
fix: linter
michalsek Nov 6, 2025
87d19f0
fix: port local fixes
michalsek Nov 7, 2025
7a7692d
fix: actually make it working
michalsek Nov 7, 2025
2038d78
fix: do not free the circular buffer on stop cleanup
michalsek Nov 7, 2025
7429baa
fix: some leftovers
michalsek Nov 17, 2025
0c8cb40
feat: file presets, cleanup, recorder start and stop failure messaging
michalsek Nov 18, 2025
79010a5
feat: its something
michalsek Nov 21, 2025
f649956
feat: android error handling
michalsek Nov 25, 2025
3c8e612
feat: cleaner ffmpeg
michalsek Nov 25, 2025
2ef7c99
fix: cleanup
michalsek Nov 25, 2025
04b54ce
fix: ffmpeg optimizations
michalsek Nov 26, 2025
7502b60
feat: recording demo app
michalsek Nov 27, 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
File renamed without changes.
2 changes: 1 addition & 1 deletion apps/common-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@
"react": "19.1.1",
"react-native": "0.82.0",
"react-test-renderer": "19.1.1",
"typescript": "5.8.3"
"typescript": "~5.8.3"
}
}
10 changes: 10 additions & 0 deletions apps/common-app/prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/** @type {import('prettier').Config} */
module.exports = {
plugins: ["prettier-plugin-jsdoc"],
bracketSameLine: false,
printWidth: 80,
singleQuote: true,
trailingComma: "es5",
tabWidth: 2,
arrowParens: "always",
};
4 changes: 2 additions & 2 deletions apps/common-app/src/components/BGGradient.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Canvas, RadialGradient, Rect, vec } from '@shopify/react-native-skia';
import React, { useCallback, useState } from 'react';
import { LayoutChangeEvent, StyleSheet, View } from 'react-native';
import { vec, Rect, Canvas, RadialGradient } from '@shopify/react-native-skia';

import { colors } from '../styles';

Expand All @@ -19,7 +19,7 @@ const BGGradient = () => {
<Canvas style={styles.canvas}>
<Rect x={0} y={0} width={size.width} height={size.height}>
<RadialGradient
r={size.width}
r={size.width / 4}
c={vec(size.width / 2, 0)}
colors={[colors.backgroundLight, colors.background]}
/>
Expand Down
12 changes: 8 additions & 4 deletions apps/common-app/src/components/Container.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React, { PropsWithChildren } from 'react';
import { SafeAreaView } from 'react-native-safe-area-context';
import { StyleProp, StyleSheet, ViewStyle } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';

import BGGradient from './BGGradient';
import { colors } from '../styles';

type ContainerProps = PropsWithChildren<{
Expand All @@ -19,8 +18,13 @@ const Container: React.FC<ContainerProps> = (props) => {
return (
<SafeAreaView
edges={['bottom', 'left', 'right']}
style={[styles.basic, centered && styles.centered, !disablePadding && styles.padding, style]}>
<BGGradient />
style={[
styles.basic,
centered && styles.centered,
!disablePadding && styles.padding,
style,
]}
>
{children}
</SafeAreaView>
);
Expand Down
13 changes: 7 additions & 6 deletions apps/common-app/src/components/icons/PlayPauseIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import React from 'react';
import Svg, { Path, Rect } from 'react-native-svg';
import { View } from 'react-native';
import Svg, { Path, Rect } from 'react-native-svg';

type Props = {
isPlaying: boolean;
size?: number;
color?: string;
};

const PlayPauseIcon: React.FC<Props> = ({ isPlaying = true, size = 48, color = '#FFFFFF' }) => {
const PlayPauseIcon: React.FC<Props> = ({
isPlaying = true,
size = 48,
color = '#FFFFFF',
}) => {
return (
<View>
<Svg width={size} height={size} viewBox="0 0 64 64" fill="none">
Expand All @@ -18,10 +22,7 @@ const PlayPauseIcon: React.FC<Props> = ({ isPlaying = true, size = 48, color = '
<Rect x="38" y="12" width="10" height="40" rx="2" fill={color} />
</>
) : (
<Path
d="M18 12L52 32L18 52V12Z"
fill={color}
/>
<Path d="M18 12L52 32L18 52V12Z" fill={color} />
)}
</Svg>
</View>
Expand Down
7 changes: 4 additions & 3 deletions apps/common-app/src/examples/AudioFile/AudioPlayer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { AudioContext, AudioManager } from 'react-native-audio-api';
import type {
AudioBufferSourceNode,
AudioBuffer,
AudioBufferSourceNode,
} from 'react-native-audio-api';
import { AudioContext, AudioManager } from 'react-native-audio-api';

class AudioPlayer {
private readonly audioContext: AudioContext;
Expand Down Expand Up @@ -93,7 +93,8 @@ class AudioPlayer {
loadBuffer = async (url: string) => {
const buffer = await fetch(url, {
headers: {
'User-Agent': 'Mozilla/5.0 (Android; Mobile; rv:122.0) Gecko/122.0 Firefox/122.0',
'User-Agent':
'Mozilla/5.0 (Android; Mobile; rv:122.0) Gecko/122.0 Firefox/122.0',
},
})
.then((response) => response.arrayBuffer())
Expand Down
10 changes: 5 additions & 5 deletions apps/common-app/src/examples/AudioVisualizer/AudioVisualizer.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, { useState, useEffect, useRef } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { ActivityIndicator, View } from 'react-native';
import {
AudioContext,
AnalyserNode,
AudioBuffer,
AudioBufferSourceNode,
AudioContext,
} from 'react-native-audio-api';
import { ActivityIndicator, View } from 'react-native';

import FreqTimeChart from './FreqTimeChart';
import { Container, Button } from '../../components';
import { Button, Container } from '../../components';
import { layout } from '../../styles';
import FreqTimeChart from './FreqTimeChart';

const FFT_SIZE = 512;

Expand Down
37 changes: 37 additions & 0 deletions apps/common-app/src/examples/Record/ControlPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { FC } from 'react';
import { StyleSheet } from 'react-native';

import Animated from 'react-native-reanimated';
import PauseButton from './PauseButton';
import RecordButton from './RecordButton';
import { RecordingState } from './types';

interface ControlPanelProps {
state: RecordingState;
onToggleState: (action: RecordingState) => void;
}

const ControlPanel: FC<ControlPanelProps> = ({ state, onToggleState }) => {
return (
<Animated.View style={styles.controlPanelView}>
<PauseButton
state={state}
onPress={() => {
onToggleState(RecordingState.Paused);
}}
/>
<RecordButton state={state} onToggleState={onToggleState} />
</Animated.View>
);
};

export default ControlPanel;

const styles = StyleSheet.create({
controlPanelView: {
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
gap: 12,
},
});
89 changes: 89 additions & 0 deletions apps/common-app/src/examples/Record/PauseButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { FC } from 'react';
import { Pressable, StyleSheet, View } from 'react-native';
import Animated, {
useAnimatedProps,
withSpring,
} from 'react-native-reanimated';
import { RecordingState } from './types';

interface PauseButtonProps {
state: RecordingState;
onPress: () => void;
}

const PauseButton: FC<PauseButtonProps> = ({ state, onPress }) => (
<View>
<Pressable onPress={onPress} style={styles.pressable}>
{({ pressed }) => <PauseButtonInner state={state} pressed={pressed} />}
</Pressable>
</View>
);

export default PauseButton;

const size = 24;
const innerSize = size * 0.3;

const PauseButtonInner: FC<{
pressed: boolean;
state: RecordingState;
}> = ({ pressed, state }) => {
const leftViewStyle = useAnimatedProps(() => {
return {
transform: [
{
translateX: withSpring(pressed ? 6 : 0),
},
],
};
});

const rightViewStyle = useAnimatedProps(() => {
return {
transform: [
{
translateX: withSpring(pressed ? -6 : 0),
},
],
};
});

const containerStyle = useAnimatedProps(() => {
return {
transform: [
{
scale: withSpring(state === RecordingState.Recording ? 1 : 0),
},
],
};
});

return (
<Animated.View style={[styles.container, containerStyle]}>
<Animated.View style={[styles.bar, leftViewStyle]} />
<Animated.View style={[styles.bar, rightViewStyle]} />
</Animated.View>
);
};

const styles = StyleSheet.create({
pressable: {
position: 'absolute',
left: -size,
top: -size / 2,
},
container: {
width: size,
height: size,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: innerSize - 2,
},
bar: {
width: innerSize,
height: size,
backgroundColor: '#d4d4d4',
borderRadius: 2,
},
});
Loading