Skip to content

Commit 4e83900

Browse files
Sound Recorder app: max duration 60min (or as much as storage allows)
1 parent 9286260 commit 4e83900

File tree

1 file changed

+79
-18
lines changed

1 file changed

+79
-18
lines changed

internal_filesystem/apps/com.micropythonos.soundrecorder/assets/sound_recorder.py

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,13 @@ class SoundRecorder(Activity):
3535
"""
3636

3737
# Constants
38-
MAX_DURATION_MS = 60000 # 60 seconds max recording
3938
RECORDINGS_DIR = "data/recordings"
39+
SAMPLE_RATE = 16000 # 16kHz
40+
BYTES_PER_SAMPLE = 2 # 16-bit audio
41+
BYTES_PER_SECOND = SAMPLE_RATE * BYTES_PER_SAMPLE # 32000 bytes/sec
42+
MIN_DURATION_MS = 5000 # Minimum 5 seconds
43+
MAX_DURATION_MS = 3600000 # Maximum 1 hour (absolute cap)
44+
SAFETY_MARGIN = 0.80 # Use only 80% of available space
4045

4146
# UI Widgets
4247
_status_label = None
@@ -57,6 +62,9 @@ class SoundRecorder(Activity):
5762
def onCreate(self):
5863
screen = lv.obj()
5964

65+
# Calculate max duration based on available storage
66+
self._current_max_duration_ms = self._calculate_max_duration()
67+
6068
# Title
6169
title = lv.label(screen)
6270
title.set_text("Sound Recorder")
@@ -69,7 +77,7 @@ def onCreate(self):
6977

7078
# Timer display
7179
self._timer_label = lv.label(screen)
72-
self._timer_label.set_text("00:00 / 01:00")
80+
self._timer_label.set_text(self._format_timer_text(0))
7381
self._timer_label.align(lv.ALIGN.CENTER, 0, -30)
7482
self._timer_label.set_style_text_font(lv.font_montserrat_24, 0)
7583

@@ -123,6 +131,9 @@ def onCreate(self):
123131

124132
def onResume(self, screen):
125133
super().onResume(screen)
134+
# Recalculate max duration (storage may have changed)
135+
self._current_max_duration_ms = self._calculate_max_duration()
136+
self._timer_label.set_text(self._format_timer_text(0))
126137
self._update_status()
127138
self._find_last_recording()
128139

@@ -170,6 +181,57 @@ def _find_last_recording(self):
170181
print(f"SoundRecorder: Error finding recordings: {e}")
171182
self._last_recording = None
172183

184+
def _calculate_max_duration(self):
185+
"""
186+
Calculate maximum recording duration based on available storage.
187+
Returns duration in milliseconds.
188+
"""
189+
try:
190+
# Ensure recordings directory exists
191+
_makedirs(self.RECORDINGS_DIR)
192+
193+
# Get filesystem stats for the recordings directory
194+
stat = os.statvfs(self.RECORDINGS_DIR)
195+
196+
# Calculate free space in bytes
197+
# f_bavail = free blocks available to non-superuser
198+
# f_frsize = fragment size (fundamental block size)
199+
free_bytes = stat[0] * stat[4] # f_frsize * f_bavail
200+
201+
# Apply safety margin (use only 80% of available space)
202+
usable_bytes = int(free_bytes * self.SAFETY_MARGIN)
203+
204+
# Calculate max duration in seconds
205+
max_seconds = usable_bytes // self.BYTES_PER_SECOND
206+
207+
# Convert to milliseconds
208+
max_ms = max_seconds * 1000
209+
210+
# Clamp to min/max bounds
211+
max_ms = max(self.MIN_DURATION_MS, min(max_ms, self.MAX_DURATION_MS))
212+
213+
print(f"SoundRecorder: Free space: {free_bytes} bytes, "
214+
f"usable: {usable_bytes} bytes, max duration: {max_ms // 1000}s")
215+
216+
return max_ms
217+
218+
except Exception as e:
219+
print(f"SoundRecorder: Error calculating max duration: {e}")
220+
# Fall back to a conservative 60 seconds
221+
return 60000
222+
223+
def _format_timer_text(self, elapsed_ms):
224+
"""Format timer display text showing elapsed / max time."""
225+
elapsed_sec = elapsed_ms // 1000
226+
max_sec = self._current_max_duration_ms // 1000
227+
228+
elapsed_min = elapsed_sec // 60
229+
elapsed_sec_display = elapsed_sec % 60
230+
max_min = max_sec // 60
231+
max_sec_display = max_sec % 60
232+
233+
return f"{elapsed_min:02d}:{elapsed_sec_display:02d} / {max_min:02d}:{max_sec_display:02d}"
234+
173235
def _generate_filename(self):
174236
"""Generate a timestamped filename for the recording."""
175237
# Get current time
@@ -200,17 +262,26 @@ def _start_recording(self):
200262
file_path = self._generate_filename()
201263
print(f"SoundRecorder: Generated filename: {file_path}")
202264

265+
# Recalculate max duration before starting (storage may have changed)
266+
self._current_max_duration_ms = self._calculate_max_duration()
267+
268+
if self._current_max_duration_ms < self.MIN_DURATION_MS:
269+
print("SoundRecorder: Not enough storage space")
270+
self._status_label.set_text("Not enough storage space")
271+
self._status_label.set_style_text_color(lv.color_hex(0xAA0000), 0)
272+
return
273+
203274
# Start recording
204275
print(f"SoundRecorder: Calling AudioFlinger.record_wav()")
205276
print(f" file_path: {file_path}")
206-
print(f" duration_ms: {self.MAX_DURATION_MS}")
207-
print(f" sample_rate: 16000")
277+
print(f" duration_ms: {self._current_max_duration_ms}")
278+
print(f" sample_rate: {self.SAMPLE_RATE}")
208279

209280
success = AudioFlinger.record_wav(
210281
file_path=file_path,
211-
duration_ms=self.MAX_DURATION_MS,
282+
duration_ms=self._current_max_duration_ms,
212283
on_complete=self._on_recording_complete,
213-
sample_rate=16000
284+
sample_rate=self.SAMPLE_RATE
214285
)
215286

216287
print(f"SoundRecorder: record_wav returned: {success}")
@@ -281,25 +352,15 @@ def _stop_timer_update(self):
281352
if self._timer_task:
282353
self._timer_task.delete()
283354
self._timer_task = None
284-
self._timer_label.set_text("00:00 / 01:00")
355+
self._timer_label.set_text(self._format_timer_text(0))
285356

286357
def _update_timer(self, timer):
287358
"""Update timer display (called periodically)."""
288359
if not self._is_recording:
289360
return
290361

291362
elapsed_ms = time.ticks_diff(time.ticks_ms(), self._record_start_time)
292-
elapsed_sec = elapsed_ms // 1000
293-
max_sec = self.MAX_DURATION_MS // 1000
294-
295-
elapsed_min = elapsed_sec // 60
296-
elapsed_sec = elapsed_sec % 60
297-
max_min = max_sec // 60
298-
max_sec_display = max_sec % 60
299-
300-
self._timer_label.set_text(
301-
f"{elapsed_min:02d}:{elapsed_sec:02d} / {max_min:02d}:{max_sec_display:02d}"
302-
)
363+
self._timer_label.set_text(self._format_timer_text(elapsed_ms))
303364

304365
def _on_play_clicked(self, event):
305366
"""Handle play button click."""

0 commit comments

Comments
 (0)