Skip to content

Commit

Permalink
major feat: default keep stream open, user can close it with cancel b…
Browse files Browse the repository at this point in the history
…uton
  • Loading branch information
braden-w committed Jun 30, 2024
1 parent ae46b89 commit 5150f97
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Data, Effect, Either } from 'effect';
import { nanoid } from 'nanoid/non-secure';
import { ToastService } from './ToastService.js';

const enumerateRecordingDevices = Effect.tryPromise({
export const enumerateRecordingDevices = Effect.tryPromise({
try: async () => {
const allAudioDevicesStream = await navigator.mediaDevices.getUserMedia({ audio: true });
const devices = await navigator.mediaDevices.enumerateDevices();
Expand All @@ -20,6 +20,7 @@ const enumerateRecordingDevices = Effect.tryPromise({
error: error,
}),
});

class GetStreamError extends Data.TaggedError('GetStreamError')<{
recordingDeviceId: string;
}> {}
Expand All @@ -28,14 +29,12 @@ class TryResuseStreamError extends Data.TaggedError('TryResuseStreamError') {}

export const MediaRecorderService = Effect.gen(function* () {
const { toast } = yield* ToastService;
const mediaStreamService = yield* MediaStreamService;
let mediaRecorder: MediaRecorder | null = null;
const recordedChunks: Blob[] = [];

const resetRecorder = () => {
recordedChunks.length = 0;
mediaRecorder = null;
mediaStreamService.destroy();
};

return {
Expand All @@ -52,7 +51,7 @@ export const MediaRecorderService = Effect.gen(function* () {
title: 'Connecting to audio input device...',
description: 'Please allow access to your microphone if prompted.',
});
const maybeResusedStream = yield* mediaStreamService.init({
const maybeResusedStream = yield* mediaStream.init({
shouldReuseStream: true,
preferredRecordingDeviceId,
});
Expand Down Expand Up @@ -84,7 +83,7 @@ export const MediaRecorderService = Effect.gen(function* () {
title: 'Error initializing media recorder with preferred device',
description: 'Trying to find another available audio input device...',
});
const stream = yield* mediaStreamService.init({ shouldReuseStream: false });
const stream = yield* mediaStream.init({ shouldReuseStream: false });
return new AudioRecorder(stream, {
mimeType: 'audio/webm;codecs=opus',
sampleRate: 16000,
Expand Down Expand Up @@ -137,8 +136,8 @@ export const MediaRecorderService = Effect.gen(function* () {
};
});

export const MediaStreamService = Effect.gen(function* () {
let internalStream: MediaStream | null = null;
export const mediaStream = Effect.gen(function* () {
let internalStream = $state<MediaStream | null>(null);

const getStreamForDeviceId = (recordingDeviceId: string) =>
Effect.tryPromise({
Expand Down Expand Up @@ -216,4 +215,4 @@ export const MediaStreamService = Effect.gen(function* () {
},
enumerateRecordingDevices,
};
});
}).pipe(Effect.runSync);
8 changes: 5 additions & 3 deletions apps/app/src/lib/stores/recorder.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { sendMessageToExtension } from '$lib/sendMessageToExtension';
import { MediaRecorderService, MediaStreamService } from '$lib/services/MediaRecorderService';
import {
MediaRecorderService,
enumerateRecordingDevices,
} from '$lib/services/MediaRecorderService.svelte';
import { NotificationServiceDesktopLive } from '$lib/services/NotificationServiceDesktopLive';
import { NotificationServiceWebLive } from '$lib/services/NotificationServiceWebLive';
import { SetTrayIconService } from '$lib/services/SetTrayIconService';
Expand Down Expand Up @@ -46,15 +49,14 @@ const IS_RECORDING_NOTIFICATION_ID = 'WHISPERING_RECORDING_NOTIFICATION';

export const recorder = Effect.gen(function* () {
const mediaRecorderService = yield* MediaRecorderService;
const mediaStreamService = yield* MediaStreamService;
const { notify } = yield* NotificationService;

return {
get recorderState() {
return recorderState.value;
},
enumerateRecordingDevices: () =>
mediaStreamService.enumerateRecordingDevices.pipe(
enumerateRecordingDevices.pipe(
Effect.catchAll((error) => {
renderErrorAsToast(error);
return Effect.succeed([] as MediaDeviceInfo[]);
Expand Down
14 changes: 13 additions & 1 deletion apps/app/src/routes/(app)/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { ClipboardIcon } from '$lib/components/icons';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { mediaStream } from '$lib/services/MediaRecorderService.svelte';
import { recorder, recordings, settings } from '$lib/stores';
import { createRecordingViewTransitionName } from '$lib/utils/createRecordingViewTransitionName';
Expand Down Expand Up @@ -65,10 +66,21 @@
onclick={() => recorder.cancelRecording(settings)}
variant="ghost"
size="icon"
class="absolute -right-16 bottom-1.5 transform text-2xl hover:scale-110 focus:scale-110"
class="absolute -right-14 bottom-0 transform text-2xl hover:scale-110 focus:scale-110"
>
🚫
</WhisperingButton>
{:else if mediaStream.isStreamOpen}
<!-- Reusing media stream for faster rerecording. Click to close stream (tab will also no longer show that it's recording) -->
<WhisperingButton
tooltipText="Quick re-record enabled. Click to end stream."
onclick={mediaStream.destroy}
variant="ghost"
size="icon"
class="absolute -right-14 bottom-0 transform text-2xl hover:scale-110 focus:scale-110"
>
🔴
</WhisperingButton>
{/if}
</div>

Expand Down

0 comments on commit 5150f97

Please sign in to comment.