├── .github └── workflows │ └── python-publish.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── audiorecorder ├── __init__.py └── frontend │ ├── .env │ ├── .prettierrc │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── bootstrap.min.css │ └── index.html │ ├── src │ ├── AudioRecorder.tsx │ ├── index.tsx │ └── react-app-env.d.ts │ └── tsconfig.json ├── example ├── example.py ├── example_whisper.py └── requirements.txt ├── images ├── buttons.gif └── visualiser.gif ├── packages.txt └── setup.py /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | deploy: 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up Node.js 26 | uses: actions/setup-node@v3 27 | with: 28 | node-version: 20 29 | - name: Install dependencies and build 30 | run: | 31 | cd audiorecorder/frontend/ 32 | npm install 33 | npm run build 34 | - name: Set up Python 35 | uses: actions/setup-python@v3 36 | with: 37 | python-version: '3.x' 38 | - name: Install dependencies 39 | run: | 40 | python -m pip install --upgrade pip 41 | pip install build 42 | - name: Build package 43 | run: python -m build 44 | - name: Publish package 45 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 46 | with: 47 | user: __token__ 48 | password: ${{ secrets.PYPI_API_TOKEN }} 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | node_modules/ 3 | build/ 4 | dist/ 5 | *.egg-info/ 6 | .DS_Store 7 | .vscode/ 8 | .github/workflows/python-publish-test.yml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2023 Streamlit Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include audiorecorder/frontend/build * 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://audio-recorder.streamlit.app) 2 | # streamlit-audiorecorder 3 | 4 | ### An audio Recorder for streamlit 5 | 6 | #### Description 7 | Audio recorder component for streamlit. 8 | It creates a button to start the recording and takes three arguments: the start button text, the stop button text, and the pause button text. 9 | If the pause button text is not specified, the pause button is not displayed. 10 | 11 |  12 | 13 | If all prompts are given as empty strings, the component will use the [react-audio-recorder](https://github.com/samhirtarif/react-audio-recorder) visualizer: 14 | 15 |  16 | 17 | #### Parameters 18 | The signature of the component is: 19 | ```python 20 | audiorecorder( 21 | start_prompt="Start recording", 22 | stop_prompt="Stop recording", 23 | pause_prompt="", 24 | custom_style={'color': 'black'}, 25 | start_style={}, 26 | pause_style={}, 27 | stop_style={}, 28 | show_visualizer=True, 29 | key=None): 30 | ``` 31 | The prompt parameters are self-explanatory. 32 | The style parameters are dictionaries that allow you to customize the appearance of the buttons using CSS. 33 | The `custom_style` parameter is applied to all buttons, while the `start_style`, `pause_style`, and `stop_style` parameters are applied only to the corresponding button. 34 | The optional `key` parameter is used internally by Streamlit to properly distinguish multiple audiorecorders on the page. 35 | The `show_visualizer` parameter is a boolean that determines whether to show live audio visualization while recording. If set to False, the text "recording" is displayed. It is used only when all prompts are empty strings. 36 | 37 | #### Return value 38 | The component's return value is a [pydub](https://github.com/jiaaro/pydub/) [`AudioSegment`](https://github.com/jiaaro/pydub/blob/master/API.markdown#audiosegment). 39 | All `AudioSegment` methods are available. In particular, you can: 40 | - Play the audio in the frontend with `st.audio(audio.export().read())` 41 | - Save the audio to a file with `audio.export("audio.wav", format="wav")` 42 | 43 | ### Installation: 44 | ```bash 45 | pip install streamlit-audiorecorder 46 | ``` 47 | Note: This package uses ffmpeg, so it should be installed for this audiorecorder to work properly. 48 | 49 | On Ubuntu/Debian: `sudo apt update && sudo apt install ffmpeg` 50 | On Mac: `brew install ffmpeg` 51 | 52 | ### Usage: 53 | ```python 54 | import streamlit as st 55 | from audiorecorder import audiorecorder 56 | 57 | st.title("Audio Recorder") 58 | audio = audiorecorder("Click to record", "Click to stop recording") 59 | 60 | if len(audio) > 0: 61 | # To play audio in frontend: 62 | st.audio(audio.export().read()) 63 | 64 | # To save audio to a file, use pydub export method: 65 | audio.export("audio.wav", format="wav") 66 | 67 | # To get audio properties, use pydub AudioSegment properties: 68 | st.write(f"Frame rate: {audio.frame_rate}, Frame width: {audio.frame_width}, Duration: {audio.duration_seconds} seconds") 69 | ``` 70 | 71 | --- 72 | ### Troubleshooting: 73 | 74 | **Error**: No record button is shown and you get the following error message in the console: 75 | ```console 76 | Component Error 77 | Cannot read properties of undefined (reading 'getUserMedia') 78 | ``` 79 | **Reason**: To record the audio, this component uses the `MediaDevices` interface. 80 | For security reasons, the `getUserMedia()` method is available only in secure contexts (HTTPS), as explained in the 81 | [MDM documentation](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) : 82 | 83 | > As an API that may involve significant privacy concerns, getUserMedia()'s specification lays out a wide array of privacy and security requirements that browsers are obligated to meet. 84 | > 85 | > getUserMedia() is a powerful feature that can only be used in secure contexts; in insecure contexts, navigator.mediaDevices is undefined, preventing access to getUserMedia(). A secure context is, in short, a page loaded using HTTPS or the file:/// URL scheme, or a page loaded from localhost. 86 | 87 | **Solution**: Serve your website using HTTPS. If you are serving your website locally, make sure to access it using `localhost`, not an IP address. 88 | 89 | 90 | -------------------------------------------------------------------------------- /audiorecorder/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import streamlit.components.v1 as components 3 | 4 | from io import BytesIO 5 | from base64 import b64decode 6 | from pydub import AudioSegment 7 | 8 | 9 | _RELEASE = True 10 | _LOAD_LOCAL = False 11 | 12 | 13 | if _LOAD_LOCAL and not _RELEASE: 14 | _component_func = components.declare_component( 15 | "audiorecorder", 16 | url="http://localhost:3001", 17 | ) 18 | else: 19 | parent_dir = os.path.dirname(os.path.abspath(__file__)) 20 | build_dir = os.path.join(parent_dir, "frontend/build") 21 | _component_func = components.declare_component("audiorecorder", path=build_dir) 22 | 23 | 24 | def audiorecorder( 25 | start_prompt="Start recording", 26 | stop_prompt="Stop recording", 27 | pause_prompt="", 28 | custom_style={}, 29 | start_style={}, 30 | pause_style={}, 31 | stop_style={}, 32 | show_visualizer=True, 33 | key=None, 34 | ) -> AudioSegment: 35 | base64_audio = _component_func( 36 | startPrompt=start_prompt, 37 | stopPrompt=stop_prompt, 38 | pausePrompt=pause_prompt, 39 | customStyle=custom_style, 40 | startStyle=start_style, 41 | pauseStyle=pause_style, 42 | stopStyle=stop_style, 43 | showVisualizer=show_visualizer, 44 | key=key, 45 | default=b"", 46 | ) 47 | audio_segment = AudioSegment.empty() 48 | if len(base64_audio) > 0: 49 | # Firefox and Chrome handle webm but Safari doesn't, so we let pydub/ffmpeg figure out the format 50 | # audio_segment = AudioSegment.from_file(BytesIO(b64decode(base64_audio)), format="webm") 51 | audio_segment = AudioSegment.from_file(BytesIO(b64decode(base64_audio))) 52 | return audio_segment 53 | 54 | 55 | if not _RELEASE: 56 | import streamlit as st 57 | 58 | st.subheader("Audio Recorder Test") 59 | audio_1 = ( 60 | audiorecorder( 61 | "Click to record", 62 | "Click to stop recording", 63 | "Click to pause", 64 | custom_style={"color": "blue", "backgroundColor": "lightgrey"}, 65 | start_style={"color": "pink", "backgroundColor": "red"}, 66 | pause_style={"color": "green"}, 67 | stop_style={"backgroundColor": "purple"}, 68 | key="audio_1", 69 | ), 70 | st.container(), 71 | ) 72 | audio_2 = audiorecorder("", "", show_visualizer=True, key="audio_2"), st.container() 73 | 74 | for audio, container in [audio_1, audio_2]: 75 | if len(audio) > 0: 76 | # To play the audio in the frontend 77 | container.audio(audio.export().read()) 78 | 79 | # To get audio properties 80 | print(audio.frame_rate) 81 | print(audio.frame_width) 82 | print(audio.duration_seconds) 83 | container.write( 84 | f"Frame rate: {audio.frame_rate}, Frame width: {audio.frame_width}, Duration: {audio.duration_seconds} seconds" 85 | ) 86 | 87 | # To save the audio 88 | # audio.export("audio.wav", format="wav") 89 | -------------------------------------------------------------------------------- /audiorecorder/frontend/.env: -------------------------------------------------------------------------------- 1 | # Run the component's dev server on :3001 2 | # (The Streamlit dev server already runs on :3000) 3 | PORT=3001 4 | 5 | # Don't automatically open the web browser on `npm run start`. 6 | BROWSER=none 7 | -------------------------------------------------------------------------------- /audiorecorder/frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /audiorecorder/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "streamlit-audiorecorder", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@theevann/react-audio-voice-recorder": "2.2.2", 7 | "@types/dom-mediacapture-record": "^1.0.11", 8 | "@types/jest": "^24.0.0", 9 | "react": "^18.3.1", 10 | "react-dom": "^18.3.1", 11 | "streamlit-component-lib": "^2.0.0" 12 | }, 13 | "resolutions": { 14 | "@types/react": "17.0.14", 15 | "@types/react-dom": "17.0.14" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start --env.development", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": "react-app" 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | }, 38 | "homepage": ".", 39 | "devDependencies": { 40 | "@babel/plugin-proposal-private-property-in-object": "^7.14.5", 41 | "@types/node": "^12.0.0", 42 | "@types/react": "17.0.14", 43 | "@types/react-dom": "17.0.14", 44 | "react-scripts": "^5.0.1", 45 | "typescript": "^4.2.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /audiorecorder/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |