오디오 재생 프로그램 만들기

유관희·2022년 10월 14일
0
post-thumbnail

1. 과제 및 어플리케이션 설명

  • 오디오를 녹음하고, 재생하는 등 오디오 파일을 관리하는 프로그램을 개발합니다.

2. 진행 과정 설명

  • 구현 중 라이브러리 및 오디오 태그 사용 최소화합니다. (CSS 관련 라이브러리는 제외)
  • 화면 별 컴포넌트 분리합니다. (그 이상 분리해도 문제 없음)
  • UI는 자유롭게 개발합니다.
  • 추가적인 기능 개발 가능합니다.
  • Optional 로 표시한 선택 구현 사항은 필수 구현 사항은 아닙니다.

3. 기술 스택

  • HTML / CSS / Javascript / React (선택 사항)

4. 필수 구현 사항

4-1. 오디오 녹음 화면


  1. 오디오 녹음 기능이 가능합니다.
  2. 오디오 녹음 중일 때에는, 녹음 중 UI가 표시되어야 합니다.
  3. 녹음 중에는 녹음이 되고 있는 시간을 표기합니다.
  4. input 값을 받아 최대 녹음 가능 시간을 control할 수 있어야 합니다.
  5. 오디오 녹음 완료 후에는 firebase firestorage를 이용하여 음성 파일을 저장합니다 (Optional, 선택 구현 사항)

4-2. 음성 재생 화면


  1. 오디오 재생 기능이 가능합니다.
  2. 오디오가 재생된 시간을 표시합니다.
  3. 오디오 파일을 다운로드 할 수 있습니다.
  4. 오디오 파형을 표시할 수 있습니다. (Optional, 선택 구현 사항)

5. 선택 구현 사항

5-1. 녹음된 음성 리스트 화면


  • 4-1. 오디오 녹음 화면 의 5번 선택 구현사항 까지 마무리 되고나서 추가 구현하는 사항입니다.
  • 저장된 오디오 파일을 불러올 수 있습니다.
  • 각 오디오 파일 별로 4-2. 음성 재생 화면의 재생 기능을 삽입합니다.

한번 만들어 보자!!!!

일단은 vscode 로 tree를 만들어 보았다.

  • 이번 프로젝트는 Mic 녹음에 관한 거여서 완전 처음 접했기에 하나하나씩 정리해야겠다.
const onRecAudio = () => {
        // 음원정보를 담은 노드를 생성하거나 음원을 실행또는 디코딩 시키는 일을 한다
        const audioCtx = new (window.AudioContext ||
            window.webkitAudioContext)();
        // 자바스크립트를 통해 음원의 진행상태에 직접접근에 사용된다.
        const analyser = audioCtx.createScriptProcessor(0, 1, 1);
        setAnalyser(analyser);

        function makeSound(stream) {
            // 내 컴퓨터의 마이크나 다른 소스를 통해 발생한 오디오 스트림의 정보를 보여준다.
            const source = audioCtx.createMediaStreamSource(stream);
            setSource(source);
            source.connect(analyser);
            analyser.connect(audioCtx.destination);
        }
        // 마이크 사용 권한 획득
        navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
            const mediaRecorder = new MediaRecorder(stream);
            mediaRecorder.start();
            setStream(stream);
            setMedia(mediaRecorder);
            makeSound(stream);

            analyser.onaudioprocess = function (e) {
                // 3분(180초) 지나면 자동으로 음성 저장 및 녹음 중지
                if (e.playbackTime > time) {
                    stream.getAudioTracks().forEach(function (track) {
                        track.stop();
                    });
                    mediaRecorder.stop();
                    // 메서드가 호출 된 노드 연결 해제
                    analyser.disconnect();
                    audioCtx.createMediaStreamSource(stream).disconnect();

                    mediaRecorder.ondataavailable = function (e) {
                        setAudioUrl(e.data);
                        setOnRec(true);
                    };
                } else {
                    setOnRec(false);
                }
            };
        });
    };

    const onSubmitAudioFile = useCallback(() => {
        if (audioUrl) {
            setSound(URL.createObjectURL(audioUrl)); // 출력된 링크에서 녹음된 오디오 확인 가능
        }
        // File 생성자를 사용해 파일로 변환
        const sound = new File([audioUrl], 'soundBlob', {
            lastModified: new Date().getTime(),
            type: 'audio/wav',
        });
        console.log(sound); // File 정보 출력
    }, [audioUrl]);

blob

blob은 Binary Large Object의 약자입니다. 이름에서 바이너리 형태로 큰 객체를 저장할 것이라는 것을 추측할 수 있습니다. 여기서 이 큰 객체라는 것은 주로 이미지, 비디오, 사운드 등과 같은 멀티미디어 객체들을 주로 가리킵니다.

var blob = new Blob([typedArray], {type: 'application/octet-binary'});
var audioBlob = new Blob([dataview], { type: type });

위와 같이 배열과 타입을 넣어서 객체를 생성합니다.

var url = URL.createObjectURL(blob);
var url = URL.createObjectURL(audioBlob);

위와 같은 형태로 URL을 생성할 수 있는데 이 URL을 (앵커) 태그에 연결시키면 다운로드를 받을 수 있습니다. 다운로드라고는 하지만 서버에서 직접 다운로드를 받는 것이 아니라 브라우저 상에 저장되어 있는 blob파일을 다운로드 받게 됩니다.

var reader = new FileReader();
reader.readAsArrayBuffer(blob);

blob파일은 FileReader를 통해서 읽을 수 있습니다.


  • 오디오 녹음, 재생 기능 구현

  • 오디오 녹음 중일 때, 녹음 중 UI 구현

  • 녹음 중에는 녹음이 되고 있는 시간 표시

  • 재생 중일 때, 재생된 시간 표시 구현

  • input값을 100단위로 받아 최대 녹음 가능 시간을 조절 기능 구현

  • 오디오 녹음 완료 후에는 "react-wavesurfer.js" 이용 Wave를 만듭니다.

  • a tag 를 이용하여 음성 파일을 저장 기능 구현

  • 오디오 파일을 다운로드 기능 구현

반응형 웹 미디어 쿼리

처음에는 JavaScript 의 media query 가 react에 적용이 안되는 줄 알았다. 왜냐하면 meta tag 때문이었다. 바로 <meta name='viewport 'content="width=device-width, initial-scal=1" /> 요녀석 때문이다. JavaScript에서는 이것을 Html의 부분에 넣어야 했기 때문이다. React 에서는 이 가 없기에 당연히 안되는 줄 알고 React의 media-quary 라이브러리를 여러개 설치해보고 실습해 보았으나 너무 어려웠다. 그래서 혹시나 하고 React의 Return() 부분에

사이에 meta tag를 넣었고 css 파일에 JavaScript css를 적용했다. 그결과 성고이었다.

@media screen and (max-width: 750px) {
    .wrapper2 > input {
      margin-top: 20px;
      display: block;
      clear: both;
      width: 65%;
    }
    .wave {
      width: 140%;
      margin-left: -110px;
    }
    .audiowarpper > audio {
      margin-left: 30px;
      width: 75%;
      /* margin: 0 auto; */
    }
  }

이렇게 JavaScript @media 를 그대로 써도 적용이 되었다. 이것을 알게 된후 일사천리로 반응형으로 update를 쉽게 할수 있었다.

후기

처음으로 녹음에 대한 코딩을 해봐서 어떻게 할까 고민하다가 이미 녹음에 대한 소스가 많이 유포 되어 있어 코드를 분석하면서 새로운 명령어와, 라이브러리를 공부하는 것이 재미있었고 내가 만든 녹음기에서 내가 마이크로 말하는 것이 녹음되어 재생 되는 것이 너무 신기했다.
애먹었던 것 중 하나는 녹음 버튼을 누른 동시에 타이머 버튼이 동시에 하나의 버튼으로 제어하는 것인데 나의 생각으로는 맞는 로직인데 계속 안되었다. 거의 하루를 애먹고 드디어 발견한 것이 내가 props로 보낸 함수에서 props를 받을 때 받는 쪽의 함수 export default function SetTimer({props}){} 에서 props를 받을 때 중가로를 안썼다는 것을 알게 되었다.

또 다른 하나는 파형을 만드는 것인데 이것은 라이르러리를 찾는 것이 너무 힘들어서 너무나 많은 소스를 clone 하여 많은 시행 착오 끝에 구현해 내어 너무 뿌듯했다.

다운로드 받는 것도 생전 처음하는 거라서 이것도 거의 하루를 애먹다가 예전 팀원의 조언으로 tag a 가 단순히 tag 가 아니라 download 하는 부분에 중요하다는 것을 알게 되었다.

profile
안녕하세요~

0개의 댓글