Skip to content
Open
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
18 changes: 9 additions & 9 deletions client/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,10 @@ packages:
dependency: transitive
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.4.1"
charcode:
dependency: transitive
description:
Expand Down Expand Up @@ -359,7 +359,7 @@ packages:
path: "../packages/flet"
relative: true
source: path
version: "0.82.2"
version: "0.85.0"
flet_ads:
dependency: "direct main"
description:
Expand Down Expand Up @@ -911,18 +911,18 @@ packages:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
version: "0.12.17"
version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.11.1"
version: "0.13.0"
media_kit:
dependency: transitive
description:
Expand Down Expand Up @@ -1628,10 +1628,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
url: "https://pub.dev"
source: hosted
version: "0.7.7"
version: "0.7.10"
torch_light:
dependency: transitive
description:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[project]
name = "services-audio-recorder-example-1"
name = "services-audio-recorder-basic"
version = "1.0.0"
description = "Records audio, lists input devices, and checks recorder permissions from one screen."
requires-python = ">=3.10"
keywords = ["audio recorder", "example 1", "services", "async"]
keywords = ["audio recorder", "basic", "services", "async"]
authors = [{ name = "Flet team", email = "hello@flet.dev" }]
dependencies = ["flet", "flet-audio-recorder"]

Expand All @@ -14,7 +14,7 @@ dev = ["flet-cli", "flet-desktop", "flet-web"]
categories = ["Services/AudioRecorder"]

[tool.flet.metadata]
title = "Audio recorder example"
title = "Audio recorder basic"
controls = ["SafeArea", "Column", "Page", "AppBar", "Text", "Button", "AudioRecorder"]
layout_pattern = "inline-actions"
complexity = "basic"
Expand Down
74 changes: 74 additions & 0 deletions sdk/python/examples/services/audio_recorder/stream/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import wave

import flet as ft
import flet_audio_recorder as far

SAMPLE_RATE = 44100
CHANNELS = 1
BYTES_PER_SAMPLE = 2
OUTPUT_FILE = "streamed-recording.wav"


def main(page: ft.Page):
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

buffer = bytearray()

def show_snackbar(message: str):
page.show_dialog(ft.SnackBar(content=message, duration=ft.Duration(seconds=5)))

def handle_stream(e: far.AudioRecorderStreamEvent):
buffer.extend(e.chunk)
status.value = (
f"Streaming chunk {e.sequence}; {e.bytes_streamed} bytes collected."
)

async def handle_recording_start(e: ft.Event[ft.Button]):
if not await recorder.has_permission():
show_snackbar("Microphone permission is required.")
return

buffer.clear()
status.value = "Recording..."
await recorder.start_recording(
configuration=far.AudioRecorderConfiguration(
encoder=far.AudioEncoder.PCM16BITS,
sample_rate=SAMPLE_RATE,
channels=CHANNELS,
),
)

async def handle_recording_stop(e: ft.Event[ft.Button]):
await recorder.stop_recording()
if not buffer:
show_snackbar("Nothing was recorded.")
return

with wave.open(OUTPUT_FILE, "wb") as wav:
wav.setnchannels(CHANNELS)
wav.setsampwidth(BYTES_PER_SAMPLE)
wav.setframerate(SAMPLE_RATE)
wav.writeframes(buffer)

status.value = f"Saved {len(buffer)} bytes to {OUTPUT_FILE}."
show_snackbar(status.value)

recorder = far.AudioRecorder(on_stream=handle_stream)

page.add(
ft.SafeArea(
content=ft.Column(
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
controls=[
ft.Text("Record PCM16 audio chunks and save them as a WAV file."),
ft.Button("Start streaming", on_click=handle_recording_start),
ft.Button("Stop and save", on_click=handle_recording_stop),
status := ft.Text(),
],
),
)
)


if __name__ == "__main__":
ft.run(main)
26 changes: 26 additions & 0 deletions sdk/python/examples/services/audio_recorder/stream/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[project]
name = "services-audio-recorder-stream"
version = "1.0.0"
description = "Records PCM16 audio chunks and saves the streamed data as a WAV file."
requires-python = ">=3.10"
keywords = ["audio recorder", "streaming", "services", "async", "save to file"]
authors = [{ name = "Flet team", email = "hello@flet.dev" }]
dependencies = ["flet", "flet-audio-recorder"]

[dependency-groups]
dev = ["flet-cli", "flet-desktop", "flet-web"]

[tool.flet.gallery]
categories = ["Services/AudioRecorder"]

[tool.flet.metadata]
title = "Audio recorder stream"
controls = ["SafeArea", "Column", "Page", "AppBar", "Text", "Button", "AudioRecorder"]
layout_pattern = "inline-actions"
complexity = "intermediate"
features = ["audio recording", "streaming chunks", "save to file", "async"]

[tool.flet]
org = "dev.flet"
company = "Flet"
copyright = "Copyright (C) 2023-2026 by Flet"
78 changes: 78 additions & 0 deletions sdk/python/examples/services/audio_recorder/upload/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import time

import flet as ft
import flet_audio_recorder as far


def main(page: ft.Page):
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

def show_snackbar(message: str):
page.show_dialog(ft.SnackBar(content=message, duration=ft.Duration(seconds=5)))

def handle_upload(e: far.AudioRecorderUploadEvent):
if e.error:
status.value = f"Upload error: {e.error}"
elif e.progress == 1:
status.value = f"Upload complete: {e.bytes_uploaded or 0} bytes."
else:
status.value = f"Uploading: {e.bytes_uploaded or 0} bytes sent."

async def handle_recording_start(e: ft.Event[ft.Button]):
if not await recorder.has_permission():
show_snackbar("Microphone permission is required.")
return

file_name = f"recordings/rec-{int(time.time())}.pcm"
try:
upload_url = page.get_upload_url(file_name=file_name, expires=600)
except RuntimeError as ex:
if "FLET_SECRET_KEY" not in str(ex):
raise
status.value = (
"Uploads require a secret key. "
"Set FLET_SECRET_KEY before running this app."
)
show_snackbar(status.value)
return

status.value = "Recording..."
await recorder.start_recording(
upload=far.AudioRecorderUploadSettings(
upload_url=upload_url,
file_name=file_name,
),
configuration=far.AudioRecorderConfiguration(
encoder=far.AudioEncoder.PCM16BITS,
channels=1,
),
)

async def handle_recording_stop(e: ft.Event[ft.Button]):
await recorder.stop_recording()
show_snackbar(
"Recording stopped. See 'uploads/recordings' folder for the recorded file."
)

recorder = far.AudioRecorder(on_upload=handle_upload)

page.add(
ft.SafeArea(
content=ft.Column(
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
controls=[
ft.Text("Record PCM16 audio and upload it as it streams."),
ft.Button("Start upload", on_click=handle_recording_start),
ft.Button("Stop recording", on_click=handle_recording_stop),
status := ft.Text(),
],
),
)
)


if __name__ == "__main__":
ft.run(
main,
upload_dir="uploads",
)
26 changes: 26 additions & 0 deletions sdk/python/examples/services/audio_recorder/upload/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[project]
name = "services-audio-recorder-upload"
version = "1.0.0"
description = "Records PCM16 audio and uploads the streamed bytes to Flet upload storage."
requires-python = ">=3.10"
keywords = ["audio recorder", "upload", "streaming", "services", "async"]
authors = [{ name = "Flet team", email = "hello@flet.dev" }]
dependencies = ["flet", "flet-audio-recorder"]

[dependency-groups]
dev = ["flet-cli", "flet-desktop", "flet-web"]

[tool.flet.gallery]
categories = ["Services/AudioRecorder"]

[tool.flet.metadata]
title = "Audio recorder upload"
controls = ["SafeArea", "Column", "Page", "AppBar", "Text", "Button", "AudioRecorder"]
layout_pattern = "inline-actions"
complexity = "intermediate"
features = ["audio recording", "streaming upload", "upload progress", "async"]

[tool.flet]
org = "dev.flet"
company = "Flet"
copyright = "Copyright (C) 2023-2026 by Flet"
6 changes: 6 additions & 0 deletions sdk/python/packages/flet-audio-recorder/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## 0.85.0

### Added

- Added PCM16 streaming to `AudioRecorder`, including `on_stream` chunks and direct upload support via `AudioRecorderUploadSettings` ([#5858](https://github.com/flet-dev/flet/issues/5858)) by @ndonkoHenri.

## 0.80.0

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
AudioRecorderConfiguration,
AudioRecorderState,
AudioRecorderStateChangeEvent,
AudioRecorderStreamEvent,
AudioRecorderUploadEvent,
AudioRecorderUploadSettings,
InputDevice,
IosAudioCategoryOption,
IosRecorderConfiguration,
Expand All @@ -19,6 +22,9 @@
"AudioRecorderConfiguration",
"AudioRecorderState",
"AudioRecorderStateChangeEvent",
"AudioRecorderStreamEvent",
"AudioRecorderUploadEvent",
"AudioRecorderUploadSettings",
"InputDevice",
"IosAudioCategoryOption",
"IosRecorderConfiguration",
Expand Down
Loading
Loading