diff --git a/src/speech.test.ts b/src/speech.test.ts index de6cc4a..3d19454 100644 --- a/src/speech.test.ts +++ b/src/speech.test.ts @@ -156,6 +156,91 @@ describe('speech function', () => { // Assert: 期待される結果を確認 expect(mockRecognitionInstance.start).toHaveBeenCalledTimes(1) + expect(mockRecognitionInstance.start).toHaveBeenCalledWith() + }) + + it('When calling start method with MediaStreamTrack, it passes the track to recognition.start', () => { + // Arrange: 操作に必要な準備 + const recognitionObj = speech({}) + const mockAudioTrack = { + kind: 'audio', + readyState: 'live', + } as MediaStreamTrack + + // Act: 結果を得るために必要な操作 + recognitionObj.start(mockAudioTrack) + + // Assert: 期待される結果を確認 + expect(mockRecognitionInstance.start).toHaveBeenCalledTimes(1) + expect(mockRecognitionInstance.start).toHaveBeenCalledWith(mockAudioTrack) + }) + + it('When calling start method with video track, it throws InvalidStateError', () => { + // Arrange: 操作に必要な準備 + const recognitionObj = speech({}) + const mockVideoTrack = { + kind: 'video', + readyState: 'live', + } as MediaStreamTrack + + // Act & Assert: 操作とアサーションを組み合わせる + expect(() => recognitionObj.start(mockVideoTrack)).toThrow(DOMException) + expect(() => recognitionObj.start(mockVideoTrack)).toThrow( + 'The provided MediaStreamTrack must be an audio track' + ) + expect(mockRecognitionInstance.start).not.toHaveBeenCalled() + }) + + it('When calling start method with ended audio track, it throws InvalidStateError', () => { + // Arrange: 操作に必要な準備 + const recognitionObj = speech({}) + const mockEndedTrack = { + kind: 'audio', + readyState: 'ended', + } as MediaStreamTrack + + // Act & Assert: 操作とアサーションを組み合わせる + expect(() => recognitionObj.start(mockEndedTrack)).toThrow(DOMException) + expect(() => recognitionObj.start(mockEndedTrack)).toThrow( + 'The provided MediaStreamTrack must be in "live" state' + ) + expect(mockRecognitionInstance.start).not.toHaveBeenCalled() + }) + + it('When audioTrack is provided in options, it uses that track on start', () => { + // Arrange: 操作に必要な準備 + const mockAudioTrack = { + kind: 'audio', + readyState: 'live', + } as MediaStreamTrack + const recognitionObj = speech({ audioTrack: mockAudioTrack }) + + // Act: 結果を得るために必要な操作 + recognitionObj.start() + + // Assert: 期待される結果を確認 + expect(mockRecognitionInstance.start).toHaveBeenCalledTimes(1) + expect(mockRecognitionInstance.start).toHaveBeenCalledWith(mockAudioTrack) + }) + + it('When both options.audioTrack and parameter audioTrack are provided, parameter takes precedence', () => { + // Arrange: 操作に必要な準備 + const optionsTrack = { + kind: 'audio', + readyState: 'live', + } as MediaStreamTrack + const parameterTrack = { + kind: 'audio', + readyState: 'live', + } as MediaStreamTrack + const recognitionObj = speech({ audioTrack: optionsTrack }) + + // Act: 結果を得るために必要な操作 + recognitionObj.start(parameterTrack) + + // Assert: 期待される結果を確認 + expect(mockRecognitionInstance.start).toHaveBeenCalledTimes(1) + expect(mockRecognitionInstance.start).toHaveBeenCalledWith(parameterTrack) }) it('When calling stop method, it stops the recognition', () => { diff --git a/src/speech.ts b/src/speech.ts index 168b14b..90e0e35 100644 --- a/src/speech.ts +++ b/src/speech.ts @@ -50,6 +50,12 @@ export interface SpeechOptions { * エラー時のコールバック */ onError?: (error: SpeechRecognitionErrorCode) => void + + /** + * 音声入力として使用するMediaStreamTrack + * 指定しない場合はデフォルトのマイクを使用 + */ + audioTrack?: MediaStreamTrack } /** @@ -111,9 +117,29 @@ export function speech(options: SpeechOptions = {}) { return { /** * 音声認識を開始 + * @param audioTrack - オプション: 使用する音声トラック */ - start: () => { - recognition.start() + start: (audioTrack?: MediaStreamTrack) => { + const trackToUse = audioTrack || options.audioTrack + + if (trackToUse) { + if (trackToUse.kind !== 'audio') { + throw new DOMException( + 'The provided MediaStreamTrack must be an audio track', + 'InvalidStateError' + ) + } + if (trackToUse.readyState !== 'live') { + throw new DOMException( + 'The provided MediaStreamTrack must be in "live" state', + 'InvalidStateError' + ) + } + // @ts-expect-error - Web Speech API仕様の新しいメソッドシグネチャ + recognition.start(trackToUse) + } else { + recognition.start() + } }, /**