Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 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
aa8e439
feat: slightly change app navigation
michalsek Dec 1, 2025
f8ee2f3
Merge branch 'main' into feat/audio-recorder-to-file
michalsek Dec 2, 2025
11316e1
fix: remove jni libs
michalsek Dec 2, 2025
2788510
fix: fabric/common example versions
michalsek Dec 2, 2025
e05de19
fix: default recorder sampleFormat
michalsek Dec 3, 2025
103aef4
Merge branch 'main' into feat/audio-recorder-to-file
michalsek Dec 3, 2025
9d29a5a
fix: pr review batch 1
michalsek Dec 3, 2025
c502d57
fix: add some comments address some issues
michalsek Dec 3, 2025
477fc90
fix: pr review batch 3
michalsek Dec 3, 2025
a132957
fix: std memory ordeal
michalsek Dec 3, 2025
4750752
fix: cleanup index
michalsek Dec 3, 2025
31f4c29
feat: robust result type
poneciak57 Dec 3, 2025
60acec4
fix: pr review batch 4
michalsek Dec 4, 2025
38768f6
fix: fixed result
poneciak57 Dec 4, 2025
f341d80
Update packages/react-native-audio-api/android/src/main/cpp/audioapi/…
michalsek Dec 4, 2025
532a4b9
Merge branch 'main' into feat/audio-recorder-to-file
michalsek Dec 4, 2025
17ff402
feat: some docs, returnstatus -> resut, poc recreate engine
michalsek Dec 4, 2025
46b6142
fix: cleanup & android adapter
michalsek Dec 4, 2025
ab46ede
fix: miniaudio error handlers, cleanup and unify the clases
michalsek Dec 5, 2025
a17e824
fix: ffmpeg error handling during writing
michalsek Dec 5, 2025
d66b665
fix: unify tabbar
michalsek Dec 5, 2025
e98e761
fix: do not expose yet unsupported property
michalsek Dec 5, 2025
9117c67
fix: filePath_ scope
michalsek Dec 8, 2025
3783d6d
fix: tests
mdydek Dec 9, 2025
911adf0
fix: 2nd tests try
mdydek Dec 9, 2025
d541ca2
fix: android recorder dtor, ios engine management
michalsek Dec 9, 2025
10b5a0d
Merge branch 'main' into feat/audio-recorder-to-file
michalsek Dec 9, 2025
918737b
fix: enum values
michalsek Dec 9, 2025
a162a61
fix: restart the engine only if not running
michalsek Dec 9, 2025
4dd9c0e
fix: properly cleanup recorder state between examples
michalsek Dec 9, 2025
67ffa5e
fix: interruptions, bring back shouldResume on both platforms
michalsek Dec 9, 2025
549baf1
fix: notification managers add isRegistered
michalsek Dec 9, 2025
f40e54b
feat: recorder docs
michalsek Dec 10, 2025
a08a4bf
fix: recorder isPaused & stop isRecording vs isIdle
michalsek Dec 10, 2025
56e2607
fix: stop conditions
michalsek Dec 10, 2025
6cb1700
Merge branch 'main' into feat/audio-recorder-to-file
michalsek Dec 10, 2025
aaf9ba2
feat: missing recorder doc sections, fix some logical issues
michalsek Dec 11, 2025
5f3cc51
feat: made result types simplier and impl more functional features
poneciak57 Dec 12, 2025
25d07df
feat: remaining code comments
michalsek Dec 13, 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.
8 changes: 5 additions & 3 deletions apps/common-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@
"react-native": "*"
},
"dependencies": {
"@react-navigation/native": "7.1.18",
"@react-navigation/bottom-tabs": "^7.8.7",
"@react-navigation/native": "7.1.22",
"@react-navigation/native-stack": "7.3.28",
"@react-navigation/stack": "7.4.10",
"@shopify/react-native-skia": "2.3.0",
"lucide-react-native": "^0.555.0",
"react-native-audio-api": "workspace:*",
"react-native-background-timer": "2.4.1",
"react-native-gesture-handler": "2.28.0",
"react-native-reanimated": "4.1.3",
"react-native-safe-area-context": "5.6.1",
"react-native-screens": "4.17.1",
"react-native-screens": "4.18.0",
"react-native-svg": "15.14.0",
"react-native-worklets": "0.6.1"
},
Expand All @@ -41,6 +43,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",
};
192 changes: 177 additions & 15 deletions apps/common-app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { Gauge, ListCheck, Waves } from 'lucide-react-native';
import type { FC } from 'react';
import React from 'react';
import {
Expand All @@ -8,22 +9,53 @@ import {
Pressable,
StyleSheet,
Text,
View,
} from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Spacer } from './components';
import Container from './components/Container';
import { demos, DemoScreen } from './demos';
import { Example, Examples, MainStackProps } from './examples';
import { colors, layout } from './styles';

const Stack = createStackNavigator();

const ItemSeparatorComponent = () => <Spacer.Vertical size={16} />;
const TestsScreen: FC = () => {
const navigation = useNavigation<MainStackProps>();

const renderItem: ListRenderItem<Example> = ({
item: { Icon, key, title },
}) => (
<Pressable
key={key}
style={styles.buttonSmall}
onPress={() => navigation.navigate(key)}
>
<Icon color={colors.white} size={18} />
<Text style={styles.titleSmall}>{title}</Text>
</Pressable>
);

return (
<Container headless>
<FlatList
data={Examples}
renderItem={renderItem}
keyExtractor={(item) => item.key}
contentContainerStyle={styles.scrollView}
ItemSeparatorComponent={ItemSeparatorComponentSmall}
numColumns={2}
/>
</Container>
);
};

const HomeScreen: FC = () => {
const DemoAppsScreen: FC = () => {
const navigation = useNavigation<MainStackProps>();

const renderItem: ListRenderItem<Example> = ({ item }) => (
const renderItem: ListRenderItem<DemoScreen> = ({ item }) => (
<Pressable
onPress={() => navigation.navigate(item.key)}
key={item.key}
Expand All @@ -32,15 +64,18 @@ const HomeScreen: FC = () => {
{ borderStyle: pressed ? 'solid' : 'dashed' },
]}
>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.subtitle}>{item.subtitle}</Text>
<item.icon color={colors.white} size={24} />
<View style={styles.buttonInner}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.subtitle}>{item.subtitle}</Text>
</View>
</Pressable>
);

return (
<Container>
<Container headless>
<FlatList
data={Examples}
data={demos}
renderItem={renderItem}
keyExtractor={(item) => item.key}
contentContainerStyle={styles.scrollView}
Expand All @@ -50,6 +85,77 @@ const HomeScreen: FC = () => {
);
};

const OtherScreen: FC = () => {
return <Container headless />;
};

const MainTabs = createBottomTabNavigator<MainStackProps>({
screens: {
Tests: TestsScreen,
DemoApps: DemoAppsScreen,
Other: OtherScreen,
},
});

const tabBarIcon = ({
routeName,
color,
size,
}: {
routeName: string;
color: string;
size: number;
}) => {
if (routeName === 'Tests') {
return <ListCheck color={color} size={size} />;
} else if (routeName === 'DemoApps') {
return <Gauge color={color} size={size} />;
} else if (routeName === 'Other') {
return <Waves color={color} size={size} />;
}
return null;
};

const MainTabsScreen: FC = () => {
return (
<MainTabs.Navigator
screenOptions={({ route }: { route: { name: string } }) => ({
tabBarIcon: ({ color, size }: { color: string; size: number }) =>
tabBarIcon({ routeName: route.name, color, size }),
tabBarActiveTintColor: '#ff7774',
tabBarInactiveTintColor: colors.border,
tabBarTransparent: true,
tabBarStyle: {
backgroundColor: colors.background,
},
headerShown: false,
})}
>
<MainTabs.Screen
name="Tests"
component={TestsScreen}
options={{
title: 'Tests',
}}
/>
<MainTabs.Screen
name="DemoApps"
component={DemoAppsScreen}
options={{
title: 'Demo Apps',
}}
/>
<MainTabs.Screen
name="Other"
component={OtherScreen}
options={{
title: 'Other',
}}
/>
</MainTabs.Navigator>
);
};

const App: FC = () => {
return (
<GestureHandlerRootView style={styles.container}>
Expand All @@ -62,14 +168,14 @@ const App: FC = () => {
backgroundColor: 'transparent',
},
headerTintColor: colors.white,
headerBackTitle: ' ',
headerBackTitle: 'Back',
headerBackAccessibilityLabel: 'Go back',
}}
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Audio API examples' }}
name="MainTabs"
component={MainTabsScreen}
options={{ headerShown: false }}
/>
{Examples.map((item) => (
<Stack.Screen
Expand All @@ -79,12 +185,32 @@ const App: FC = () => {
options={{ title: item.title }}
/>
))}
{demos.map((item) => (
<Stack.Screen
key={item.key}
name={item.key}
component={item.screen}
options={{ title: item.title }}
/>
))}
</Stack.Navigator>
</NavigationContainer>
</GestureHandlerRootView>
);
};

export default App;

const ItemSeparatorComponent = () => <Spacer.Vertical size={16} />;

const ItemSeparatorComponentSmall = () => (
<View>
<Spacer.Vertical size={6} />
<View style={styles.hr} />
<Spacer.Vertical size={6} />
</View>
);

const styles = StyleSheet.create({
container: {
flex: 1,
Expand All @@ -93,6 +219,7 @@ const styles = StyleSheet.create({
fontSize: 24,
fontWeight: '700',
color: colors.white,
lineHeight: 24,
},
subtitle: {
opacity: 0.6,
Expand All @@ -104,10 +231,45 @@ const styles = StyleSheet.create({
borderRadius: layout.radius,
paddingVertical: layout.spacing * 2,
paddingHorizontal: layout.spacing * 2,
flexDirection: 'row',
alignItems: 'flex-start',
gap: layout.spacing,
},
buttonInner: {
flexShrink: 1,
},
buttonSmall: {
padding: layout.spacing,
flexDirection: 'row',
alignItems: 'center',
gap: layout.spacing,
width: '48%',
margin: '1%',
height: 40,
borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.white,
},
titleSmall: {
fontSize: 16,
fontWeight: '600',
color: colors.white,
marginLeft: 4,
flexShrink: 1,
},
subtitleSmall: {
opacity: 0.6,
color: colors.white,
},
scrollView: {
padding: layout.spacing * 2,
scrollView: {},
hr: {
// height: StyleSheet.hairlineWidth,
// // backgroundColor: colors.border,
// marginLeft: 60,
// marginRight: '35%',
},
row: {
flexDirection: 'row',
alignItems: 'center',
gap: layout.spacing,
},
});

export default App;
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
28 changes: 21 additions & 7 deletions apps/common-app/src/components/Container.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
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<{
style?: StyleProp<ViewStyle>;
centered?: boolean;
disablePadding?: boolean;
headless?: boolean;
}>;

const headerPadding = 120; // eyeballed

const Container: React.FC<ContainerProps> = (props) => {
const { children, style, centered, disablePadding } = props;
const { children, style, centered, disablePadding, headless } = props;

return (
<SafeAreaView
edges={['bottom', 'left', 'right']}
style={[styles.basic, centered && styles.centered, !disablePadding && styles.padding, style]}>
<BGGradient />
edges={
headless
? ['bottom', 'left', 'right', 'top']
: ['bottom', 'left', 'right']
}
style={[
headless ? styles.basicHeadless : styles.basic,
centered && styles.centered,
!disablePadding && styles.padding,
style,
]}
>
{children}
</SafeAreaView>
);
Expand All @@ -34,8 +43,13 @@ const styles = StyleSheet.create({
paddingTop: headerPadding,
backgroundColor: colors.background,
},
basicHeadless: {
flex: 1,
backgroundColor: colors.background,
paddingTop: 20,
},
padding: {
padding: 24,
paddingHorizontal: 18,
},
centered: {
alignItems: 'center',
Expand Down
Loading