[카테고리:] 생활코딩

  • daemon 예제

    linux system programming, 174p 예제 실행.

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <linux/fs.h>
    
    int main (void)
    
    {
    	pid_t pid, sid;
    	int i;
    	int j=0;
    	/* create new process */
    	pid = fork ();
    	printf("Pid is %d \n",pid);
    	if (pid == -1)
    		return -1;
    	else if (pid != 0)
    		exit (EXIT_SUCCESS);
    
    
    	/* create new session */
    	sid =setsid();
    	if (sid == -1)
    		return -1;
    
    	printf("sid is %d \n",sid);
    	/* set the working directory to the root directory */
    	if (chdir("/") == -1){
    		printf("check");
    		return -1;
    	}
    
    	/* close all open files--NR_OPEN is overkill, but works */
    	printf("FOPEN_MAX is %d\n",FOPEN_MAX);
    	for (i = 0; i < FOPEN_MAX; i++){
    		//j=close(i);
    		close(i);
    		//printf("%d\n", j);
    		printf("%d is closed\n", i);
    	}
    
    	/* redirect fd's 0,1,2 to /dev/null */
    	open("/dev/null", O_RDWR); /* stdin */
    	dup(0); /* stdout */
    	dup(0); /* stderror */
    	/* do its daemon thing... */
    
    	while(1){
    
    		sleep(1);
    		//printf("this is test\n");
    	}
    
    	return 0;
    
    }
    

    54번 행 printf가 나오지 않음은 stdout을 죽여서 그런 듯 하다.

    NR_OPEN을 찾을 수 없는데, 옛날 프로그램이라 다른 문구로 정의된 듯 하다. FOPEN_MAX로 일단 했다.

    pi@raspberrypi:~/Project/cCode/systemProgram $ gcc daemon.c ;./a.out;ps aux | grep pi;
    Pid is 1939 
    Pid is 0 
    sid is 1939 
    FOPEN_MAX is 16
    0 is closed
    message+   346  0.0  0.0   6736  3524 ?        Ss    6월22   0:04 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
    avahi      350  0.0  0.0   5896  2960 ?        Ss    6월22   0:02 avahi-daemon: running [raspberrypi.local]
    pi         846  0.0  0.1  14716  7204 ?        Ss    6월22   0:12 /lib/systemd/systemd --user
    pi         849  0.0  0.0  16864  1772 ?        S     6월22   0:00 (sd-pam)
    pi         891  0.0  0.0   7256  3568 tty1     S+    6월22   0:00 -bash
    pi        1939  0.0  0.0   1852    64 ?        Ss   19:01   0:00 ./a.out
    pi        1940  0.0  0.0   8552  2532 pts/1    R+   19:01   0:00 ps aux
    pi        1941  0.0  0.0   6116   496 pts/1    S+   19:01   0:00 grep --color=auto pi
    root     28022  0.0  0.1  12236  6304 ?        Ss   17:16   0:00 sshd: pi [priv]
    pi       28033  0.0  0.0  12236  3532 ?        S    17:16   0:01 sshd: pi@pts/0
    pi       28036  0.0  0.0   7392  3856 pts/0    Ss   17:16   0:00 -bash
    pi       28244  0.1  0.1  11564  6728 pts/0    S+   17:19   0:06 vi daemon.c
    root     28246  0.0  0.1  12236  6252 ?        Ss   17:19   0:00 sshd: pi [priv]
    pi       28257  0.0  0.0  12236  3552 ?        S    17:19   0:00 sshd: pi@pts/1
    pi       28260  0.0  0.0   7548  3924 pts/1    Ss   17:19   0:00 -bash
    

    pid 1939가 ?로 대몬으로 변신했다.

  • 교통사고 분석

    교통사고를 분석해 봤다.

    • 노인은 일단 치면 사망률이 높다.
    • 무단횡단보다 노인을 치는 사고가 많다.
    • 무단횡단, 노인 관련, 스쿨존 사고가 사고 다발지역에서 발생하면 사망률이 높다.

    wordpress에 jupyter notebook 삽입은 여기에서 찾았다.

  • jupyter matplot 한글 폰트 설정

    몇 번 헤매다 답을 찾았다.

    https://stackoverflow.com/questions/42097053/matplotlib-cannot-find-basic-fonts

    I had this exact same problem on a Vagrant VM running Ubuntu Xenial 64-bit. No matter how many fonts I had already installed, matplotlib was having a problem with the “system” font name “sans-serif”. I fixed it by:

    • Stopping Jupyter
    • Installing font-manager: sudo apt install font-manager
    • Cleaning the matplotlib cache directory: rm ~/.cache/matplotlib -fr
    • Restarting Jupyter.

    No more error messages about sans-serif.

    해당 코드에 아래 두 줄을 삽입한다. 폰트는 찾아서 해당 위치에 넣어 준다.

    font_name = font_manager.FontProperties(fname="/usr/share/fonts/truetype/nanum/NanumGothic.ttf").get_name()
    rc('font', family=font_name)
  • tensorflow 2.1.0 compile

    tensorflow 2.1.0 compile

    남는 시간에 tensorflow 2.0을 컴파일에 도전했다. 현실은 실패하여 2.1로 목표 재설정. docker 이미지를 사용하면 쉬운데, avx2를 지원하지 않는 CPU를 사용하여 선택할 수 없다. 직접 컴파일하지 않는 한 사용할 수 없다. 최근 개발 이미지를 찾아보니 cuda 10.1, python3 이었다. nvidia-driver는 여러 버전이 설정 되었는데, host pc 버전으로 구동하나 보다. bazel 버전은 3.0이다.

    bazel 버전을 2.1.0에 맞는 0.27로 바꿨다. compile 하면 약 6시간 정도 걸린다. 문제가 몇 개 있었다. 전에 램 12GB로 컴파일을 성공했다. 지금 8GB로 해보니, 10,000번 스텝넘어 동작을 멈추고 실패했다. 램을 사기 애매하여 일단 SWAP을 30GB 만들어 컴파일에 성공했다. 그런데 상당히 느리다. 결국 램 8GB를 구매했다. 그런데도 firefox를 띄우고 컴파일하면 에러난다.

    메뉴얼은 host에 CUDA를 설치할 필요 없다고 했다. nvidia-driver를 440 버전으로 업데이트 하면 CUDA 10.2를 기본 설치한다. 이게 뭐가 문제냐면 docker gpu 이미지가 10.2을 지원하지 않아 gpu를 사용할 수 없다. 일단 nvidia docker가 10.1 이미지를 띄우면 cuda 10.0으로 내릴 수 없다. 드라이버도 같이 내려야 하는데, 사용 중이어 수정할 수 없나보다. 실패하여 cuda 10.1 이미지를 사용했다. 그러나 nvidia-driver-440으로 cuda 10.1을 사용할 수 없다. 결국 host pc 드라이버를 438로 내렸다.

    nvidia driver version 440.82는 CUDA 10.2를 기본 설치한다.

    tensorflow 1.12 버전은 host pc driver 440으로 잘 구동한다. 왜 9.0은 실행하는데 10.x 버전을 실행하지 못하는지 모르겠다.

    3일째 이 미친 짓을 하고 있다. 뭘 위해서 인지 모르겠다. 지금까지 버린 인건비와 전기 요금을 생각하면 PC 1/3대를 구입했다.

    결국 다음 조합으로 컴파일에 성공했다.

    • ubuntu 18.04
    • nvidia-driver 435, cuda 10.1
    • docker image: latest-devel-gpu-py3, cuda 10.1, python 3.

    컴파일 거의 마지막 단계에 host python 버전 2와 guest python 버전 3 환경 설정값 다름으로 컴파일을 실패했다. 여기를 참조하여 간단하게 tensorRT를 사용하지 않도록 설정했다. 어차피 내 그래픽 카드는 지원하지 않는다. python2 버전 이미지로 해도 될 듯 하다.

    ERROR: /tensorflow_src/tensorflow/tensorflow/python/keras/api/BUILD:129:1: Executing genrule //tensorflow/python/keras/api:keras_python_api_gen_compat_v2 failed (Exit 1)
    Traceback (most recent call last):
      File "/root/.cache/bazel/_bazel_root/51f904752746bc15a93061eb1cc3b8cc/execroot/org_tensorflow/bazel-out/host/bin/tensorflow/python/keras/api/create_tensorflow.python_api_2_keras_python_api_gen_compat_v2.runfiles/org_tensorflow/tensorflow/python/tools/api/generator/create_python_api.py", line 27, in <module>nvidia.com/compute/cuda/repos/ubuntu1804/x86_64  cuda-npp-10-1 10.1.243-1 [54.9 MB]
    Get:9 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64  cuda-libraries-10-1 10.1.243-1 [2588 B]
    Get:10 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64  cuda-nvrtc-dev-10-1 10.1.243-1 [8812 B]
    
        from tensorflow.python.tools.api.generator import doc_srcs
      File "/root/.cache/bazel/_bazel_root/51f904752746bc15a93061eb1cc3b8cc/execroot/org_tensorflow/bazel-out/host/bin/tensorflow/python/keras/api/create_tensorflow.python_api_2_keras_python_api_gen_compat_v2.runfiles/org_tensorflow/tensorflow/python/__init__.py", line 85, in <module>
        from tensorflow.python.ops.standard_ops import *
      File "/root/.cache/bazel/_bazel_root/51f904752746bc15a93061eb1cc3b8cc/execroot/org_tensorflow/bazel-out/host/bin/tensorflow/python/keras/api/create_tensorflow.python_api_2_keras_python_api_gen_compat_v2.runfiles/org_tensorflow/tensorflow/python/ops/standard_ops.py", line 117, in <module>
        from tensorflow.python.compiler.tensorrt import trt_convert as trt
      File "/root/.cache/bazel/_bazel_root/51f904752746bc15a93061eb1cc3b8cc/execroot/org_tensorflow/bazel-out/host/bin/tensorflow/python/keras/api/create_tensorflow.python_api_2_keras_python_api_gen_compat_v2.runfiles/org_tensorflow/tensorflow/python/compiler/tensorrt/__init__.py", line 22, in <module>
        from tensorflow.python.compiler.tensorrt import trt_convert as trt
      File "/root/.cache/bazel/_bazel_root/51f904752746bc15a93061eb1cc3b8cc/execroot/org_tensorflow/bazel-out/host/bin/tensorflow/python/keras/api/create_tensorflow.python_api_2_keras_python_api_gen_compat_v2.runfiles/org_tensorflow/tensorflow/python/compiler/tensorrt/trt_convert.py", line 28, in <module>
        from tensorflow.compiler.tf2tensorrt import wrap_py_utils
      File "/root/.cache/bazel/_bazel_root/51f904752746bc15a93061eb1cc3b8cc/execroot/org_tensorflow/bazel-out/host/bin/tensorflow/python/keras/api/create_tensorflow.python_api_2_keras_python_api_gen_compat_v2.runfiles/org_tensorflow/tensorflow/compiler/tf2tensorrt/wrap_py_utils.py", line 28, in <module>
        _wrap_py_utils = swig_import_helper()
      File "/root/.cache/bazel/_bazel_root/51f904752746bc15a93061eb1cc3b8cc/execroot/org_tensorflow/bazel-out/host/bin/tensorflow/python/keras/api/create_tensorflow.python_api_2_keras_python_api_gen_compat_v2.runfiles/org_tensorflow/tensorflow/compiler/tf2tensorrt/wrap_py_utils.py", line 24, in swig_import_helper
        _mod = imp.load_module('_wrap_py_utils', fp, pathname, description)
      File "/usr/lib/python3.6/imp.py", line 243, in load_module
        return load_dynamic(name, filename, file)
      File "/usr/lib/python3.6/imp.py", line 343, in load_dynamic
        return _load(spec)
    ImportError: /root/.cache/bazel/_bazel_root/51f904752746bc15a93061eb1cc3b8cc/execroot/org_tensorflow/bazel-out/host/bin/tensorflow/python/keras/api/create_tensorflow.python_api_2_keras_python_api_gen_compat_v2.runfiles/org_tensorflow/tensorflow/compiler/tf2tensorrt/_wrap_py_utils.so: undefined symbol: _ZN15stream_executor14StreamExecutor18EnablePeerAccessToEPS0_
    ----------------
    Note: The failure of target //tensorflow/python/keras/api:create_tensorflow.python_api_2_keras_python_api_gen_compat_v2 (with exit code 1) may have been caused by the fact that it is a Python 2 program that was built in the host configuration, which uses Python 3. You can change the host configuration (for the entire build) to instead use Python 2 by setting --host_force_python=PY2.
    
    If this error started occurring in Bazel 0.27 and later, it may be because the Python toolchain now enforces that targets analyzed as PY2 and PY3 run under a Python 2 and Python 3 interpreter, respectively. See https://github.com/bazelbuild/bazel/issues/7899 for more information.
    ----------------
    Target //tensorflow/tools/pip_package:build_pip_package failed to build
    Use --verbose_failures to see the command lines of failed build steps.
    INFO: Elapsed time: 26069.299s, Critical Path: 545.35s
    INFO: 26668 processes: 26668 local.
    FAILED: Build did NOT complete successfully

    드디어 2.1 컴파일을 성공했다. 한 번 컴파일 끝내기 위해 7시간씩 썼다. 총 5번은 실패했고 6번째 성공했다. 업데이트 전 제대로 실행됨을 확인했다. update 하고 실행하니 또 에러 뜬다. cuda 10.2 문제임을 여기에서 확인했다. 다시 버전을 내렸다.

    2020-04-28 11:13:07.200201: E tensorflow/stream_executor/cuda/cuda_blas.cc:238] failed to create cublas handle: CUBLAS_STATUS_NOT_INITIALIZED
    2020-04-28 11:13:07.202605: E tensorflow/stream_executor/cuda/cuda_blas.cc:238] failed to create cublas handle: CUBLAS_STATUS_NOT_INITIALIZED
    2020-04-28 11:13:07.202635: W tensorflow/stream_executor/stream.cc:2041] attempting to perform BLAS operation using StreamExecutor without BLAS support
    2020-04-28 11:13:07.202674: W tensorflow/core/common_runtime/base_collective_executor.cc:217] BaseCollectiveExecutor::StartAbort Internal: Blas GEMM launch failed : a.shape=(10000, 4), b.shape=(4, 1024), m=10000, n=1024, k=4
    	 [[{{node dense_1/MatMul}}]]
    Traceback (most recent call last):
      File "200428getSortedValuev3.py", line 198, in <module>
        model2.fit(x=fixed_sentence_by_index, y=training_result_asarray, epochs=10000, verbose=2, validation_split=0.3, callbacks=callbacks_list, batch_size=10000, shuffle=True)
      File "/usr/local/lib/python3.6/dist-packages/keras/engine/training.py", line 1239, in fit
        validation_freq=validation_freq)
      File "/usr/local/lib/python3.6/dist-packages/keras/engine/training_arrays.py", line 196, in fit_loop
        outs = fit_function(ins_batch)
      File "/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/keras/backend.py", line 3727, in __call__
        outputs = self._graph_fn(*converted_inputs)
      File "/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/eager/function.py", line 1551, in __call__
        return self._call_impl(args, kwargs)
      File "/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/eager/function.py", line 1591, in _call_impl
        return self._call_flat(args, self.captured_inputs, cancellation_manager)
      File "/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/eager/function.py", line 1692, in _call_flat
        ctx, args, cancellation_manager=cancellation_manager))
      File "/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/eager/function.py", line 545, in call
        ctx=ctx)
      File "/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/eager/execute.py", line 67, in quick_execute
        six.raise_from(core._status_to_exception(e.code, message), None)
      File "<string>", line 3, in raise_from
    tensorflow.python.framework.errors_impl.InternalError:  Blas GEMM launch failed : a.shape=(10000, 4), b.shape=(4, 1024), m=10000, n=1024, k=4
    	 [[node dense_1/MatMul (defined at /usr/local/lib/python3.6/dist-packages/keras/backend/tensorflow_backend.py:3009) ]] [Op:__inference_keras_scratch_graph_1128]
    
    Function call stack:
    keras_scratch_graph

    미국 국방부가 리눅스로 무기 체계를 개발한다고 한다. 오픈소스를 사용하려면 이런 저런 문제점을 모두 직접 해결해야 한다. 정말 없는 길을 만들어 간다. 기업이 왜 오픈소스로 서비스하지 않는지 알 만하다. 구글같은 능력있는 회사정도 되야 오픈소스로 서비스 할 만하다. 누가 오픈소스로 서비스 한다고 하면 능력자라 인식해야겠다.

  • mp3 태그로 plexmedia server 정보 업데이트(2)

    mp3 태그로 plexmedia server 정보 업데이트(2)

    Plex로 음악을 들으면 좋은데, tag를 관리하기 어렵다. 심지어 플레이 리스트도 맘대로 뽑아낼 수 없다. plex로 관리하는 생각을 포기하고 mp3에 있는 id3를 직접 관리하기로 했다. 아래 코드 중 mp3로 변환하는 부분을 byte freaks 에서 사용했다.

    #!/bin/bash
    #for FILE in *.webm; do
    #   echo -e "Processing video '\e[32m$FILE\e[0m'";
    #   ffmpeg -i "${FILE}" -vn -ab 320k -ar 44100 -y "${FILE%.webm}.mp3";
    #one;
    
    
    #file이름을 수정.
    total=$(ls *.mp3 | wc -l)
    #echo $total
    trackNo=0
    for FILE in *.mp3;
    do
    	trackNo=$(expr $trackNo + 1)
    	#echo $trackNo
    	SONG=$(basename "$FILE" | cut -d"-" -f2|sed 's/^_//'| sed 's/_Official_Video//'|sed 's/_//g')
    	ALBUM="Evolution"
    	ARTIST="AlexisFfrench"
    	mYearDate="2018-08-31"
    	#echo $SONG
    	#echo $ALBUM
    	#echo $ARTIST
    	mid3v2 -t "$SONG" -A "$ALBUM" -a "$ARTIST" -y "$mYearDate" -T "$trackNo/$total" "$FILE"
    
    done

    이미지는 같은 폴더에 covor.jpg나 다른 이름으로 넣어주면 잘 찾는다. 앨범 이름이 같더라도 track에 번호가 없으면 같은 앨범으로 인식하지 않는다. 모두 수정 후 메타데이터 새로 고침하면 다시 읽는다. 자가 격리 기간에 앨범이나 정리해야지.

    하루 종일 해 본 결과 Plex가 너무 까탈스럽다고 결론 냈다. 게다가 각 회사마다 id3 태그를 지 멋대로 붙여 같은 앨범으로 인식시키는데 애 먹었다. 같은 앨범이더라도 장르가 다르면 다른 앨범으로 인식한다. 트랙도 겹치면 다른 앨범으로 본다. plex가 스캔하면 id3 정보를 자기 데이터 베이스로 저장하는데, id3 태그를 수정하더라도 지가 가진 정보를 변경하지 않는다. 권한을 6->0->6으로 주어 다시 인식시켰다. 결론은 각 폴더에 앨범을 설명하는 파일을 넣어주고, 스크립트가 파일을 읽어 일괄 업데이트 하는 형식이 가장 낫다고 냈다. 파일 이름에 작곡가, 곡 이름을 정리하고 이를 basename으로 정리했다. 나머지는 찾아서 details.txt에 넣었다. 앨범 커버는 동일 디렉토리에 cover.jpg로 넣으면 된다.

    다음 details.txt 파일은 앨범 폴더에 있다. 구분자는 스페이스가 아니라 탭 이다.

    SONGEXP	$(basename $FILE | cut -d'-' -f2 | sed 's/OfficialVideo//' | sed 's/\.mp3//')
    ARTISTEXP	$(basename $FILE | cut -d'-' -f1)
    ALBUMEXP	"Evolution"
    mYearDateEXP	"2018-08-31"
    GENREEXP	"Classical"

    이 파일을 참조하여 업데이트 하는 스크립트는 다음과 같다. 나중에 활용하려 COMMENT는 남겼다. youtube-dl를 –no-playlist로 하면 해당 곡만 다운로드 할 수 있다.

    #!/bin/bash
    #for FILE in *.webm; do
    #   echo -e "Processing video '\e[32m$FILE\e[0m'";
    #   ffmpeg -i "${FILE}" -vn -ab 320k -ar 44100 -y "${FILE%.webm}.mp3";
    #done;
    
    #FILE="AlexisFfrench-AMomentInTime-woETd5QW52E.mp3"
    #Details.txt 파일에 형식 정의 ^I로 구분됨.
    #SONGEXP: 곡 형식
    total=$(ls *.mp3 | wc -l)
    trackNo=0
    
    for FILE in *.mp3;
    do
    	trackNo=$(expr $trackNo + 1)
    	SONGEXP=$(cat ./details.txt | grep SONGEXP | cut -d'	' -f2)
    	#변수에서 읽은 expression을 실행하여 다시 변수에 할당.
    	eval song="$SONGEXP"
    	ARTISTEXP=$(cat ./details.txt | grep ARTISTEXP| cut -d'	' -f2)
    	#echo $ARTISTEXP
    	eval artist="$ARTISTEXP"
    	ALBUMEXP=$(cat ./details.txt | grep ALBUMEXP| cut -d'	' -f2)
    	eval album="$ALBUMEXP"
    	mYearDateEXP=$(cat ./details.txt | grep mYearDateEXP| cut -d'	' -f2)
    	eval myeardate="$mYearDateEXP"
    	GENREEXP=$(cat ./details.txt | grep GENREEXP| cut -d'	' -f2)
    	eval genre="$GENREEXP"
    	#echo $song
    	#echo $artist
    	#echo $album
    	#echo $myeardate
    	#echo $genre
    	mid3v2 -t "$song" -A "$album" -a "$artist" -y "$myeardate" -T "$trackNo/$total" --TCON $genre --TPE2 "$artist" "$FILE"
    
    done
    
    << COMMENT
    
    
    #file이름을 수정.
    total=$(ls *.mp3 | wc -l)
    echo $total
    #echo $total
    trackNo=0
    for FILE in *.mp3;
    do
    	trackNo=$(expr $trackNo + 1)
    	#echo $trackNo
    	#SONG=$(basename "$FILE" | cut -d"-" -f2|sed 's/^_//'| sed 's/_Official_Video//'|sed 's/_//g' | sed 's/\.flac//'|sed 's/^[0-9]\{1,2\}//'| sed 's/\.mp3//')
    	SONG=$(basename "$FILE" | cut -d'-' -f2| sed 's/\.mp3//'| sed 's/^ //g'|sed 's/^[0-9]\{1,2\} //g')
    	#SONG=$(basename "$FILE" | cut -d'-' -f1-2| sed 's/\.flac//'| sed 's/^ //g'|sed 's/^[0-9]\{1,2\} //')
    	#ARTIST=$(basename "$FILE" | cut -d'-' -f1 | cut -d'.' -f2)
    	#ARTIST="Leszek Mozdzer"
    	#ARTIST="Idil Biret"
    	ARTIST="다양한가수들"
    	#ARTIST="Ivo Pogorelich"
    	#ALBUM="Chopin Piano Favourites"
    	#ALBUM="The Best of Chopin"
    	ALBUM="가요추천1500"
    	#ALBUM="Chopin Recital"
    	#mYearDate="1985-02-15"
    	mYearDate="2020-04-11"
    	#echo $trackNo
    	#echo "$SONG"
    	#echo "$ALBUM"
    	#echo "$ARTIST"
    	#echo "$total"
    	#mid3v2 -t "$SONG" -A "$ALBUM" -a "$ARTIST" -y "$mYearDate" -T "$trackNo/$total" --TCON Classical --TPE2 "$ARTIST" "$FILE"
    	mid3v2 -A "$ALBUM" -a "$ARTIST" -T "$trackNo/$total" --TCON "Other" --TPE1 "$ARTIST" --TIT2 "$SONG" "$FILE"
    
    	#mid3v2 -T "$trackNo/$total" "$FILE"
    
    done
    COMMENT
    

    유투브에서 음원을 다운로드 하여 개인적으로 들으면 불법이 아니라고 한다.(–;) 구매하는 음원도 내가 정리해야 하는데 굳이 돈 주고 사야할지 모르겠다. 스트리밍으로 듣다가 음원을 구해 듣는 방식이 더 싸겠다.