ㅇㅅㅇ

PiFaceCAD를 이용한 MP3와 라디오 만들기 - 2 본문

프로그래밍/라즈베리파이

PiFaceCAD를 이용한 MP3와 라디오 만들기 - 2

Lugun 2017. 4. 2. 10:55

4. MPD플레이어에서 playlist 만들기


mpd플레이어를 깔기만해서는 음악이 막 나오진 않는다. 우리가 손봐줬던 mpd설정파일의 초반부분에 음악이 저장될 디렉토리를 지정하는 과정이 있었다. 


나는 /home/pi/music 에 저장하겠다고 적어두었는데 아직 pi폴더에는 music이라는 폴더가 없다.


폴더를 만들어 보자.

$ cd~

$ mkdir music

$ chmod 755 music

이렇게 폴더를 만드는데 성공했다면 이제 이 폴더에 음악을 넣어 주어야 한다.


라즈베리파이에 파일을 옮기는 방법에는 여러가지가 있는데


  1.  USB
  2.  이메일 파일 첨부
  3.  클라우드 서비스 이용(dropbox, naver cloud 등등)
  4.  FTP사용


이 정도가 되겠다. 자신이 편한 방법으로 옮겨주면 된다.


옮기기에 완료하면 음악파일을 인식할 수 있도록 restart와 update를 해주어야 한다.

$ sudo /etc/init.d/mpd restart

$ mpc update

이제 노래는 다 넣어 주었으니 playlist를 만들어야 한다.

$ mpc add "파일제목".mp3

mpc add를 사용하여 추가를 할 수 있다.


그런데 노래가 한두곡도 아니고 저렇게 해선 한나절 걸린다.

$ mpc add *.mp3

이렇게 적어주면 모든 mp3파일이 다들어 간다. 혹시 추가하는데 에러가 생긴다면 분명 설정에서 삑난게 있다고 99%확신한다.


여튼 이렇게 노래들을 추가한뒤

$ mpc playlist

를 입력해보자. 노래의 목록이 뜨는 것을 확인할 수 있다.


이걸 하나의 목록으로 만들어서 다른 이름으로 저장하고 싶다. 그렇게 저장해서 필요할 때 마다 불러오는 것이다.


다행이 mpc에서는 그걸 지원한다.

$ mpc save musiclist

$ mpc playlist musiclist

$ mpc clear

$ mpc playlist

$ mpc load musiclist

일부러 하나씩 확인 할 수 있게 나눠서 적어두었다. save는 현재 playlist의 곡들을 다른 이름으로 묶어서 저장하는것이고,


이게 정상적으로 되었다면 mpc playlist "자신이 정한 이름" 을 했을 때 palylist와 같은 목록이 뜨게 된다.


그 다음엔 playlist를 깔끔하게 지우고 우리가 만든 리스트를 불러오는 과정이다.


일련의 과정을 마치면서 문제가 안생겼으면 이번엔 라디오 주소를 추가해 보자.


위와 같이 하되 추가하는 과정에서 mp3처럼 편하게는 하지 못하고, 일일히 주소를 적어주어야 한다.





5. 소스코드 작성하기


이제 진짜 준비작업은 다 끝냈으니 코드 작성을 할 시간이다!! 


이 파일은 내 코드 전체이다. 필요하면 받아서 사용하면 된다. mp3.py


이제 주석을 읽어보면서 천천히 따라 적어보자


1
2
3
4
5
import pifacecad    # pifacecad사용
import time        #time 사용(현재시간 출력 및 시간체크)
import datetime    # 현재시간 출력에 사용
import subprocess    # 파이썬에서 터미널에 명령을 주기 위해서 사용
from time import sleep #딜레이가 필요한 경우 사용
cs

처음은 필요한 모듈을 import해주는 과정이다. 저기서 잘 모를 수 있을 만한건 subprocess정도일 것이라고 생각한다.


subprocess는 코드를 통해서 터미널상에 명령을 입력해 주는 것이다. 즉 파이썬을 통해서 콘솔창을 컨트롤 할 수 있는 것이다.


subprocess는 그정도로 알면 될 것 같고, 그 다음으로는 프로그램상에 필요한 변수들을 선언해 주었다.

1
2
3
4
5
6
7
witch = 999    # 스위치 인터럽트가 발생했을 때 이곳에 스위치의 번호가 들어감
remote = 999    # 리모컨 인터럽트가 발생했을 때 이곳에 리모컨의 config가 들어감
off_flag = 999    # 프로그램을 종료하기 위한 flag이다
flag = 0        # play와 stop을 위한 flag이다
mode_flag = 0    # mp3와 radio를 선택하기위한 flag이다
mute = 0        # 음소거 상태인지를 mute를 통해 판단한다
cnt = 0        # LCD의 흐름기능 구현을 위해 선언해준 변수이다
cs

이 코드에서 설명할만한 부분은 remote인데 리모컨에서 인터럽트가 발생하면 우리가 전에 설정한 lircrc파일에서 config에 해당되는 값이 반환된다.


변수는 여기까지 이고 이번엔 함수 설명이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 스위치 인터럽트가 발생하면 이 함수 실행
def update_pin_text(event):
    global switch
    switch = event.pin_num
 
# 리모컨 인터럽트가 발생하면 이 함수 실행
def print_ir_code(event):
    global remote
    remote = int(event.ir_code)
 
# pifacecad에서 스위치에 대한 인터럽트
listener = pifacecad.SwitchEventListener(chip=cad)
for i in range(8):
    listener.register(i, pifacecad.IODIR_FALLING_EDGE, update_pin_text)
listener.activate()
 
# pifacecad에서 리모컨에 대한 인터럽트
remote_listener = pifacecad.IREventListener(prog='irexec')
for i in range(10):
    remote_listener.register(str(i), print_ir_code)
remote_listener.activate()
cs


코드중에서 가장 중요하다고 생각되는 부분이다. 리모컨 버튼이나, piface의 버튼을 눌렀을 때 인터럽트가 발생하고, 이 때 switch에 event.pin_num(버튼의 번호)를 저장해준다. 리모컨도 마찬가지로 remote에 리모컨 버튼의 config값을 넣어준다.




1
2
3
4
5
def load_time() :
    time = datetime.datetime.now()
    nowTime = time.strftime('%H:%M')
    cad.lcd.set_cursor(111)
    cad.lcd.write(nowTime)
cs

load_time()은 현재시간을 받아와서 표시해 주는 역할을 한다.



1
2
3
4
5
6
7
8
def current_lcd():
    current = subprocess.check_output("mpc current", universal_newlines=True, shell=True)
    load_time()
    print_volume(0)
    if mute == 1:
        volume_shape_mute()
    elif mute == 0:
        volume_shape()
cs

current_lcd()는 lcd에 곡명, 볼륨의 크기, 현재시간을 표시해주는 함수이다.


여기서 subprocess.check_output은 명령어를 실행한 뒤 그 결과를 반환해주는데, 그걸 이용해서 현재 노래의 제목을 받아와 lcd에 표시하도록 구현했다. mpc current라는 명령을 사용하면 노래 제목이 나오게 된다.


그 아래쪽에서는 if문을 통해 음소거 상태를 판별하여 상황에 맞는 스피커 모양을 출력한다.



1
2
3
4
5
6
7
8
9
10
11
def volume_shape():
    cad.lcd.set_cursor(0,1)
    shape = pifacecad.LCDBitmap([0b00001,0b00011,0b00111,0b11111,0b11111,0b00111,0b00011,0b00001])
    cad.lcd.store_custom_bitmap(0,shape)
    cad.lcd.write_custom_bitmap(0)
 
def volume_shape_mute():
    cad.lcd.set_cursor(0,1)
    shape = pifacecad.LCDBitmap([0b00001,0b00011,0b00101,0b11001,0b11001,0b00101,0b00011,0b00001])
    cad.lcd.store_custom_bitmap(1,shape)
    cad.lcd.write_custom_bitmap(1)
cs

위의 current_led()에서 사용한 volume_shape() 함수들이다. 막 숫자가 많아서 뭔가 싶을수도 있지만 저걸 ','마다 잘라서 차례대로 붙이면

00001        00001

00011        00011 

00111        00101

11111        11001

11111         11001

00111        00101

00011        00011

00001        00001


이렇게 스피커의 모양이 나오게 되고 이걸 store에 저장해서 출력한다는 뜻이다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def print_volume(mode):
    if mode == 0:
        status = subprocess.check_output("mpc status", universal_newlines=True, shell=True)
        vol_idx = status.find("volume")
        volume = status[vol_idx+7:vol_idx+11]
        cad.lcd.set_cursor(1,1)
        cad.lcd.write(volume)
    
    elif mode == 1:
        status = subprocess.check_output("mpc status", universal_newlines=True, shell=True)
        vol_idx = status.find("volume")
        volume = status[vol_idx+7:vol_idx+10]
        
    if volume[0== " ":
        volume = volume[1:]
        return volume
cs

print_volume()은 현재 음량이 얼마나 되는 지를 출력해주는 함수이다. 터미널에서 mpc status를 입력하면 곡의 제목부터 시작해서 음량, 현재 상태, 시간 등등 많은 정보들이 나온다.


나는 그 중에서 volume을 뽑아내서 현재 음량이 몇%인지 출력하였다.


그리고 함수의 인자로 mode가 있는데 이 mode는 정보를 출력할건지 가져올 건지에 따라서 달라진다. mode 0은 lcd에 볼륨을 출력하기 위한 것이고, mode 1은 현재 볼륨의 수치가 필요할때 사용하는 모드이다. 


마지막 if문은 만약 볼륨의 값을 다뤄야 하는 일이 생길 때 사용하려고 만들어둔 것이다. 


보통 볼륨값을 빼오면 " 50%" 이런 식으로 앞에 공백이 하나 생긴다.


이 공백을 삭제 해주기 위한 코드이다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def flow_title():
    global cnt
    global Time
    
    current = subprocess.check_output("mpc current", universal_newlines=True, shell=True)
    
    if len(current) > 16 and time.time()-Time > 0.3:
        cad.lcd.set_cursor(0,0)
        cad.lcd.write(current[cnt:])
        Time = time.time()
        cnt = cnt+1
        if cnt >= (len(current) - 16):
            cnt = 0
    elif len(current) <= 16:
        cad.lcd.set_cursor(0,0)
        cad.lcd.write(current)
cs

만드는데 고민 좀 했었던 flow_title()함수이다. 이 함수는 만약 노래 제목이 lcd의 가로 최대길인 16을 초과했을 때 한칸씩 밀리면서 제목을 전부다 보여주는 기능이다.(흐름기능)


저 함수에 들어가기전 메인에서는 Time이라는 변수에 time.time()을 저장한다.


time.time()은 1970년 1월 1일 자정 이후의 누적된 초의 정보를 float형태로 반환하는데 그걸 Time에다가 저장해 둔 것이다.


그리고 함수 안에서는 현재의 time.time()이 Time보다 얼마나 큰지 빼기를 통해서 검사를 한다.


이 수치가 0.3을 넘었을 때, 즉 0.3초가 지났을때  current[cnt]로 부터 끝까지 출력을 하게 된다.


출력을 한 이후에는 Time을 다시 현재 시간으로 설정해주고 cnt를 하나 증가시켜, 다음 번에 다시 if문 안으로 들어오면 current[cnt]번부터 쭈욱 출력하는 것이다.


이게 반복되면서 전체의 노래 제목을 다 볼 수 있게되고 제목의 끝까지 다 보여주었으면 다시 cnt를 0으로 초기화 시켜 다시 보여주게한다.




이제 함수부분은 다 보았고 메인 부분이 남아있다. 주석을 상세히 달아 두었으니 한번 읽어보면 충분히 이해 할 수 있다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
while True:    #무한반복
    
 
    if off_flag == :            # off_flag가 1이되면 lcd의 빛을 다 꺼준다
        cad.lcd.backlight_off()
        break
    
    cad.lcd.set_cursor(0,0)        # 커서를 (0,0)의 위치로 옮긴다
    cad.lcd.write("1:mp3 2:radio")    # lcd에 원하는 문구 출력
    load_time()                    # 현재시간 표시
 
    # mp3모드 선택
    if switch == or remote == 1# 스위치 1번 또는 리모컨 config 1번이 눌렸을 때
                                   
        cad.lcd.clear()                # lcd를 clear해준다
        cad.lcd.write("mp3 mode")    # mp3모드임을 표시
        sleep(1)                    # 1초간 기다렸다가 다음 동작에 들어간다
        
        cad.lcd.clear()                # lcd clear
        
        # mpc의 playlist를 비우고 musiclist를 불러온 뒤 재생
        subprocess.Popen("mpc clear", shell = True)        
        subprocess.Popen("mpc load musiclist", shell = True)
        subprocess.Popen("mpc play", shell = True)
        
        current_lcd()    # lcd에 현재상태 출력
        
        switch = 999    # 인터럽트가 발생하고 그에 관한 함수를 사용한 뒤 반드시 초기화가 필요함
        remote = 999    # 마찬가지
        mode_flag = 1    # 현재 모드가 1이라는 것을 알려줌. 모드 선택 버튼을 누르면 그 때 초기화
        
    # radio모드 선택 - 위와 매우 유사하므로 설명 생략
    elif switch == or remote == 2:
        
        cad.lcd.clear()
        cad.lcd.write("radio mode")
        sleep(1)
        
        cad.lcd.clear()
        
        subprocess.Popen("mpc clear", shell = True)
        cad.lcd.write("Loading....")
        sleep(1)
        subprocess.Popen("mpc load radiolist", shell = True)
        subprocess.Popen("mpc play", shell = True)
        
        cad.lcd.clear()
        current_lcd()
        
        switch = 999
        remote = 999
        mode_flag = 2
    
    # 종료버튼을 누르면 off_flag를 1로 올려준다
    elif switch == or remote == 8:
        cad.lcd.clear()
        cad.lcd.backlight_off()
        off_flag = 1
    
    # 모드가 선택되었으면 그 때 아래로 내려감
    if mode_flag != :
        
        Time = time.time() # 흐름기능을 구현하기위해 Time이란 변수 사용
        
        while True:
        
            current_lcd()    # 인터럽트가 발생하기 전까진 항상 이 while문을 돌기 때문에
            flow_title()    # 현재 상태를 주기적으로 출력해주면서 흐름기능을 포함시킨다.
                    
            # Play        
            if (switch == or remote == 1and flag == :
                subprocess.Popen("mpc play", shell = True)
                
                switch = 999
                remote = 999
                flag = 0
            
            # Stop
            elif (switch == or remote == 2and flag == :
                subprocess.Popen("mpc stop", shell = True)
                
                switch = 999
                remote = 999
                flag = 1
            
            # Next            
            elif switch == or remote == :
                subprocess.Popen("mpc next", shell = True)
                cad.lcd.clear()
                
                switch = 999
                remote = 999
                
            #Prev    
            elif switch == or remote == :
                subprocess.Popen("mpc prev", shell = True)
                cad.lcd.clear()
                
                switch = 999
                remote = 999
            
            # Volume_up
            elif switch == or remote == :
                subprocess.Popen("mpc volume +5", shell = True)
                
                switch = 999
                remote = 999
                
            # Volume_down    
            elif switch == or remote == :
                subprocess.Popen("mpc volume -5", shell = True)
                
                switch = 999
                remote = 999
            
            # Mute
            elif switch == or remote == :
                if mute == :    # 음소거 상태
                    str = "mpc volume " + print_volume(1# print_volume()을 사용해서 현재 볼륨을 가져온다.
                                                          # 그리고 mpc volume과 합치면 "mpc volume 85"
                                                          # 이런식의 명령어를 만들 수 있다.
                    subprocess.Popen("mpc volume 0", shell = True)
                    mute = 1
                
                elif mute == 1:    # 음소거 해제
                    subprocess.Popen(str, shell = True)    # 위에서 만든 str로 원래의 볼륨상태로 만들어준다.
                    mute = 0
                    
                switch = 999
                remote = 999
                
            # return mode select    
            elif switch == or remote == :
                subprocess.Popen("mpc stop", shell=True)
                cad.lcd.clear()
                
                switch = 999
                remote = 999
                mode_flag = 0
                break
                
            
            # exit program        
            elif switch == or remote == :
                subprocess.Popen("mpc stop", shell=True)
                cad.lcd.clear()
        
                off_flag = 1
                mode_flag = 0
                break
            
subprocess.Popen("pkill python", shell=True)    # 파이썬을 완전히 종료한다    
cs



완성이다! 이제 한번 실행 시켜보자 잘 동작할 것이다.








라즈베리파이로 mp3와 라디오를 만드는 방법에 대해서 최대한 상세히 적으려고 노력했는데 잘 전달이 되었는지 모르겠다. 


막막한 사람들에게 조금이라도 도움이 되었으면 한다. 


데모 동영상을 올리면서 이만 마치겠다.




6. 실행 동영상



버튼동작




리모컨 동작




버튼과 리모컨 동시에 사용 가능하다.


Comments