Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
118 changes: 118 additions & 0 deletions apps/common-app/src/examples/Midi/Midi.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React, { JSX } from 'react';
import { MIDIInput, requestMIDIAccess } from 'react-native-medi';
import { Button, View, Text, ScrollView } from 'react-native';
import { Container } from '../../components';
import { colors } from '../../styles';


function getColoredMidiMessage(data: Uint8Array): JSX.Element {
// Map MIDI message types to colors
// each message is 3 bytes: [status, data1, data2]
// statuses:
// 128, 135 - Note Off (red)
// 144, 151 - Note On (green)
// 176, 183 - Control Change (dark purple)
// others - default color
const status = data[0];
const RED = '#FF5555';
const GREEN = '#55FF55';
const DARK_PURPLE = '#AA00AA';
const DEFAULT_COLOR = '#AAAAAA'; // gray

let color;
if (status >= 128 && status <= 135) {
color = RED;
} else if (status >= 144 && status <= 151) {
color = GREEN;
} else if (status >= 176 && status <= 183) {
color = DARK_PURPLE;
} else {
color = DEFAULT_COLOR;
}

return <Text style={{ color }}>{`${Array.from(data).join(', ')}`}</Text>;
}

const Medi: React.FC = () => {
const [sourcePorts, setSourcePorts] = React.useState<MIDIInput[]>([]);
const [connectedPort, setConnectedPort] = React.useState<MIDIInput | null>(null);
const [messageLog, setMessageLog] = React.useState<JSX.Element[]>([]);
const scrollViewRef = React.useRef<ScrollView>(null);

const scanMIDIDevices = async () => {
const access = await requestMIDIAccess();
setSourcePorts(access.inputs);
};

const connectToPort = async (portId: string) => {
if (connectedPort) {
await connectedPort.close();
}
const access = await requestMIDIAccess();
const input = access.inputs.find((port) => port.id === portId);
if (input) {
await input.open();
input.onmidimessage = (message: Uint8Array) => {
setMessageLog((prevLog) => [...prevLog, getColoredMidiMessage(message)]);
};
setConnectedPort(input);
console.log(`Connected to MIDI Input Port: ${portId}`);
} else {
console.log(`MIDI Input Port not found: ${portId}`);
setConnectedPort(null);
}
};

const disconnectPort = async () => {
if (connectedPort) {
await connectedPort.close();
setConnectedPort(null);
setMessageLog([]);
console.log(`Disconnected from MIDI Input Port`);
}
}

return (
<Container>
<View style={{ flex: 1, alignItems: 'center' }}>
<View style={{ width: '80%' }}>
<Button title="Scan MIDI Devices" onPress={scanMIDIDevices} />
<View style={{ flexDirection: 'column', gap: 10, marginTop: 10 }}>
{connectedPort ? (
<Button title="Disconnect MIDI Device" onPress={disconnectPort} />
) : (
sourcePorts.map((port) => (
<Button key={port.id} title={`Connect to ${port.name}`} onPress={async () => await connectToPort(port.id)} />
))
)}
</View>
</View>
<View style={{ marginTop: 20, borderWidth: 1, borderColor: '#000', padding: 10, borderRadius: 5, width: '80%', height: 300 }}>
<Text style={{ fontWeight: 'bold', textAlign: 'center', color: colors.white }}>Connected Port: {connectedPort ? connectedPort.name : 'None'}</Text>
<ScrollView
ref={scrollViewRef}
style={{ width: '100%', marginTop: 10 }}
onContentSizeChange={() => scrollViewRef.current?.scrollToEnd({ animated: true })}
>
{messageLog.map((msg, index) => (
<View key={index}>
{msg}
</View>
))}
</ScrollView>
</View>
<View style={{ marginTop: 20, width: '80%' }}>
<Button
title="Clear Logs"
onPress={() => {
setMessageLog([]);
}}
/>
</View>
</View>

</Container>
);
};

export default Medi;
8 changes: 8 additions & 0 deletions apps/common-app/src/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Record from './Record/Record';
import PlaybackSpeed from './PlaybackSpeed/PlaybackSpeed';
import Worklets from './Worklets/Worklets';
import Streaming from './Streaming/Streaming';
import MediTest from './Midi/Midi';

type NavigationParamList = {
Oscillator: undefined;
Expand All @@ -25,6 +26,7 @@ type NavigationParamList = {
Record: undefined;
Worklets: undefined;
Streamer: undefined;
MediTest: undefined;
};

export type ExampleKey = keyof NavigationParamList;
Expand All @@ -38,6 +40,12 @@ export interface Example {
}

export const Examples: Example[] = [
{
key: 'MediTest',
title: 'Medi Test',
subtitle: 'Test react-native-medi turbo module',
screen: MediTest,
},
{
key: 'DrumMachine',
title: 'Drum Machine',
Expand Down
44 changes: 38 additions & 6 deletions apps/fabric-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,34 @@ PODS:
- hermes-engine (0.81.0):
- hermes-engine/Pre-built (= 0.81.0)
- hermes-engine/Pre-built (0.81.0)
- Medi (0.1.0):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- RCT-Folly (2024.11.18.00):
- boost
- DoubleConversion
Expand Down Expand Up @@ -2342,7 +2370,7 @@ PODS:
- React-perflogger (= 0.81.0)
- React-utils (= 0.81.0)
- SocketRocket
- RNAudioAPI (0.9.0):
- RNAudioAPI (0.10.0):
- boost
- DoubleConversion
- fast_float
Expand All @@ -2368,10 +2396,10 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNAudioAPI/audioapi (= 0.9.0)
- RNAudioAPI/audioapi (= 0.10.0)
- SocketRocket
- Yoga
- RNAudioAPI/audioapi (0.9.0):
- RNAudioAPI/audioapi (0.10.0):
- boost
- DoubleConversion
- fast_float
Expand All @@ -2397,10 +2425,10 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNAudioAPI/audioapi/ios (= 0.9.0)
- RNAudioAPI/audioapi/ios (= 0.10.0)
- SocketRocket
- Yoga
- RNAudioAPI/audioapi/ios (0.9.0):
- RNAudioAPI/audioapi/ios (0.10.0):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -2764,6 +2792,7 @@ DEPENDENCIES:
- fmt (from `../../../node_modules/react-native/third-party-podspecs/fmt.podspec`)
- glog (from `../../../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- Medi (from `../../../node_modules/react-native-medi`)
- RCT-Folly (from `../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTDeprecation (from `../../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
- RCTRequired (from `../../../node_modules/react-native/Libraries/Required`)
Expand Down Expand Up @@ -2860,6 +2889,8 @@ EXTERNAL SOURCES:
hermes-engine:
:podspec: "../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
:tag: hermes-2025-07-07-RNv0.81.0-e0fc67142ec0763c6b6153ca2bf96df815539782
Medi:
:path: "../../../node_modules/react-native-medi"
RCT-Folly:
:podspec: "../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
RCTDeprecation:
Expand Down Expand Up @@ -3015,6 +3046,7 @@ SPEC CHECKSUMS:
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
hermes-engine: e7491a2038f2618c8cd444ed411a6deb350a3742
Medi: bcdcf2b531371b6ad4a479cd13447759d50ec791
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
RCTDeprecation: 0735ab4f6b3ec93a7f98187b5da74d7916e2cf4c
RCTRequired: 8fcc7801bfc433072287b0f24a662e2816e89d0c
Expand Down Expand Up @@ -3081,7 +3113,7 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: c91900fa724baee992f01c05eeb4c9e01a807f78
ReactCodegen: 8125d6ee06ea06f48f156cbddec5c2ca576d62e6
ReactCommon: 116d6ee71679243698620d8cd9a9042541e44aa6
RNAudioAPI: e08a4157527a2e87879a7bb61880276a0dfc77f6
RNAudioAPI: bddcd2fba8e78164d56ac1f38d23b8cff7923600
RNGestureHandler: 3a73f098d74712952870e948b3d9cf7b6cae9961
RNReanimated: a035264789d1f64cb5adba7085d6aac6e9ec70a7
RNScreens: 6ced6ae8a526512a6eef6e28c2286e1fc2d378c3
Expand Down
3 changes: 2 additions & 1 deletion apps/fabric-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"dependencies": {
"common-app": "workspace:*",
"react": "19.1.0",
"react-native": "0.81.0"
"react-native": "0.81.0",
"react-native-medi": "workspace:*"
},
"devDependencies": {
"prettier": "^3.3.3"
Expand Down
41 changes: 41 additions & 0 deletions apps/medi-expo-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files

# dependencies
node_modules/

# Expo
.expo/
dist/
web-build/
expo-env.d.ts

# Native
.kotlin/
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision

# Metro
.metro-health-check*

# debug
npm-debug.*
yarn-debug.*
yarn-error.*

# macOS
.DS_Store
*.pem

# local env files
.env*.local

# typescript
*.tsbuildinfo

# generated native folders
/ios
/android
Loading