저번에 PiFaceCAD를 이용해 만들었던 음악플레이어에 이어서 인터넷 라디오 스트리머를 만들어보자.

먼저 PiFaceCAD를 위한 SW설치, IR Remote 설정, Configuring LIRC 는 저번 음악플레이어 만들기와 동일하니 혹시 안했다면 아래 링크에 가서 한번 해보자.


2017/03/29 - [Raspberry] - PiFaceCAD를 이용해 MusicPlayer 만들기



인터넷 라디오 스트리밍하기

인터넷 라디오 스트리밍을 위해 mplayer를 사용할 것이기 때문에 설치해 주자.
$ sudo apt-get install mplayer

간단하게 다음 코드를 입력해보자.

$ mplayer mms://211.181.136.136/livefm

여러 줄로 뭐라고 뜨며 라디오가 재생된다면 성공이다. 인터넷 라디오는 환경에 따라 되는 주소도 아닌 주소도 있기 때문에 아래의 링크 혹은 검색을 통해 더 많은 주소들을 찾아보자.


http://changjhyun.tistory.com/35


mplayer 사용하기

mplayer 를 위와 같은 방식이 아닌 slave모드로 켜면 mplayer 가 실행되는 도중 command를 읽어 들여 일시정지나 볼륨조정 같은 설정을 할 수 있다. 또는 FIFO 파일을 통해 mplayer를 조종할 수 있다.

$ mkfifo /tmp/mplayer-control $ mplayer -slave -input file=/tmp/mplayer-control mms://211.181.136.136/livefm

위와 같이 mplayer를 실행 시키면 fifo 파일을 읽어 mplayer를 조종할 수 있게 된다.


위 명령을 실행시킨 후 다른 터미널 창을 하나 더 켜서 다음 명령어를 입력해보자

$ echo "pause" > /tmp/mplayer-control


다른 터미널에서 실행되던 mplayer가 일시 정지하는가? 일시정지 했을 것이다. 이것으로 fifo파일로 mplayer를 조종 할 수 있는것을 확인할 수 있다, 위에서 사용한 pause 함수 말고도 mplayer가 slave 모드로 실행 되었을 때 사용할 수 있는 함수들이 많다. 따라서 아래 링크를 따라가 documents를 확인하여 사용할 함수들을 알아보도록 한다.


http://www.mplayerhq.hu/DOCS/tech/slave.txt




파이썬에서 mplayer 실행시키기

python에서 mplayer를 실행시키는 것 뿐만아니라, python코드에서 터미널에 명령을 내리고 싶다면 어떻게 해야할까? 파이썬의 라이브러리에서 이와 같은 경우를 위해 os.system 함수를 제공하고 있다. os.system함수의 인자로 문자열을 주면, 해당 문자열을 터미널에 치는 것과 같은 결과가 나타나게 된다.


터미널 창에 mplayer 명령을 치는 것과 같은 효과가 있다.



 따라서 mplayer를 조종할 fifo 파일에 보낼 echo 명령도 os.system 함수를 통해 구현하면 된다. 그러나 코드안에서 mplayer를 실행시켰다 해도 mplayer가 실행되는 동안에는 코드가 os.system("mplayer ...")의 다음 줄로 넘어가지 못한다. 따라서 mplayer를 실행시키는 동안 스레드를 통해 echo 명령을 내려 mplayer를 조종하도록 구현하겠다.



이를 바탕으로 인터넷 라디오 스트리머를 구현해 보았다.

curNum = 0

channelLink = ["mms://211.181.136.136/livefm", "mms://114.108.140.39/magicfm_live",\
				"mms://live.febc.net/LiveFm","mms://vod.ysmbc.co.kr/YSFM",\
				"mms://121.254.230.3/FMLIVE","mms://210.96.79.102/Incheon"]
channelName = ["MBC", "SBS","SeoulFBEC","YeosooMBC","JIBS","TBN"]
cad.lcd.write('CH : '+channelName[curNum])

path = '/tmp/mplayer-control'
pin = -1
remote = -1
mute = False
play = True
volume = 50

def radioMainFunc():
	global pin
	global remote
	global volume
	global play
	global mute
	
	f = open(path,'w')
	if mute:
		f.write('volume 0 1\n')
	else:
		f.write('volume '+ str(volume) +' 1\n')
	f.close()
	
	play = True;
	curCh = 0
	cad.lcd.clear()
	writeBottomLine()
	cad.lcd.write('CH : '+channelName[curCh])

	while True:
		showTime()
		# play control
		if pin == 2 or remote == 2:		# pause and unpause
			os.system('echo \"pause\" > /tmp/mplayer-control')
			if play:
				play = False;
			else:
				play = True;
			writeBottomLine()
			pin = -1
			remote = -1
		elif pin == 3 or remote == 3:	# next channel
			curCh = (curCh + 1) % len(channelLink)
			os.system('echo \"loadfile '+channelLink[curCh] +'\" > /tmp/mplayer-control')
			cad.lcd.clear()
			writeBottomLine()
			cad.lcd.write('CH : '+channelName[curCh])
			pin = -1
			remote = -1
		elif pin == 1 or remote == 1:	# prev channel
			if curCh == 0:
				curCh = len(channelLink)-1
			else:
				curCh -= 1
			os.system('echo \"loadfile '+channelLink[curCh] +'\" > /tmp/mplayer-control')
			cad.lcd.clear()
			writeBottomLine()
			cad.lcd.write('CH : '+channelName[curCh])
			pin = -1
			remote = -1
		# volume control
		elif pin == 6 or remote == 6:	# volume down
			while True:
				volume -= 3
				if volume < 0:
					volume = 0
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				updateVolume();
				sleep(0.2)
				remote = -1
				if not cad.switches[6].value:
					break
			pin = -1
		elif pin == 7 or remote == 7:	# volume up
			while True:
				volume += 3
				if volume > 100 :
					volume = 100
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				updateVolume();
				sleep(0.2)
				remote = -1
				if not cad.switches[7].value:
					break
			pin = -1
		elif pin == 5 or remote == 5:	# mute
			if mute:
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				mute = False;
			else:
				os.system('echo \"volume 0 1\" > /tmp/mplayer-control')
				mute = True;
			updateVolume();
			pin = -1
			remote = -1
		elif pin == 4 or remote == 4: 
				pin = -1
				remote = -1
				os.system('echo \"quit\" > /tmp/mplayer-control')
				return

main = threading.Thread(target=radioMainFunc)
main.start()

os.system('mplayer -slave -input file='+path+' '+channelLink[0])

 radioMainFunc() 이외의 함수는 저번 음악플레이어를 만들었을 때 사용한 함수들을 그대로 사용하였다. 인터럽트 또한 이전과 같은 방식으로 작성하고 사용하였다. PiFace CAD의 버튼과 리모콘도 거의 같은 역할을 한다. 달라진 점이라고는 스레드를 사용한 것과 mplayer를 조종할 때 fifo파일과, echo 명령을 사용한다는 것이다.


인터럽트 반환 값이 2일 때 : echo "pause" > [path]  명령을 통해 일시정지 및 재생

1,3일 때 : echo "loadfile" [url] > [path] 명령을 통해 이전채널, 다음채널 스트리밍

5,6,7일 때 : echo "volume" <value> 1  > [path] 명령을 통해 볼륨 조정(5: 음소거 및 해제, 6: 감소, 7: 증가)

4  일 떄 : echo "quit"] > [path]  명령을 통해 mplayer 종료




 이 코드와 저번에 작성한 음악플레이어 코드를 병합 및 수정하여 라디오와 음악플레이어를 선택하여 들을 수 있는 플레이어를 구현하였다.

from pygame import mixer # Load the required library
import pifacecad
import time
import datetime
import threading
import os


cad = pifacecad.PiFaceCAD()
cad.lcd.backlight_on()
cad.lcd.blink_off()
cad.lcd.cursor_off()

# bitmap
bmp_note = pifacecad.LCDBitmap([2,3,2,2,14,30,12,0])
bmp_play = pifacecad.LCDBitmap([0,8,12,14,12,8,0,0])
bmp_pause = pifacecad.LCDBitmap([0,27,27,27,27,27,0,0])
bmp_clock = pifacecad.LCDBitmap([0,14,21,23,17,14,0,0])
bmp_vol1 = pifacecad.LCDBitmap([1,3,15,15,15,3,1,0])
bmp_vol2 = pifacecad.LCDBitmap([8,16,0,24,0,16,8,0])

cad.lcd.store_custom_bitmap(0, bmp_note)
cad.lcd.store_custom_bitmap(1, bmp_play)
cad.lcd.store_custom_bitmap(2, bmp_pause)
cad.lcd.store_custom_bitmap(3, bmp_clock)
cad.lcd.store_custom_bitmap(4, bmp_vol1)
cad.lcd.store_custom_bitmap(5, bmp_vol2)

# music
song = ['We Don\'t Talk Anymore.mp3','Closer.mp3','Shape Of You.mp3','Starving.mp3','The Ocean.mp3'\
		,'Happy.mp3','Daft Punk.mp3', 'Chandelier.mp3','The Greatest.mp3', 'Lean On.mp3']

# radio
path = '/tmp/mplayer-control'
channelLink = ["mms://211.181.136.136/livefm", "mms://114.108.140.39/magicfm_live",\
				"mms://live.febc.net/LiveFm","mms://vod.ysmbc.co.kr/YSFM",\
				"mms://121.254.230.3/FMLIVE","mms://210.96.79.102/Incheon"]
channelName = ["MBC", "SBS","SeoulFBEC","YeosooMBC","JIBS","TBN"]


pin = -1		# PiFaceCAD button
remote = -1		# IR remote control button
volume = 50		# current volume
play = True		# current playing status (playing : True, pause: False)
mute = False	# current mute status
curSong = 0		# current song number
stridx = 0		# current printed song string index

def update_pin_text(event):
	 global pin
	 pin = event.pin_num

def print_ir_code(event):
	 global remote
	 remote = int(event.ir_code)
	 
def showTime():		# print current hour and minute
	now = datetime.datetime.now()
	nowTime = now.strftime('%H:%M')
	cad.lcd.set_cursor(10,1)
	cad.lcd.write_custom_bitmap(3)
	cad.lcd.write(nowTime)
	cad.lcd.set_cursor(0,0)
	
def writeVolume():	# print current volume
	global volume
	global mute
	cad.lcd.set_cursor(0,1)
	cad.lcd.write_custom_bitmap(4)
	cad.lcd.write_custom_bitmap(5)
	if mute:
		cad.lcd.write('mute')
	else:
		cad.lcd.write(str(volume).rjust(4))
	
def updateVolume():
	global volume
	cad.lcd.set_cursor(2,1)
	if mute:
		cad.lcd.write('mute')
	else:
		cad.lcd.write(str(volume).rjust(4))
	
	
def writeBottomLine():	#print LCD bottom line
	global play
	showTime()
	writeVolume()
	cad.lcd.set_cursor(7,1)
	if play:
		cad.lcd.write_custom_bitmap(1)
	else:
		cad.lcd.write_custom_bitmap(2)
	cad.lcd.set_cursor(0,0)
	
	
def writeTopLine():		#print LCD top line and rotate when length > 16
	global curSong
	global stridx
	
	cad.lcd.set_cursor(0,0)
	str = song[curSong]
	if len(str)>16:
		str_rotate = (str+'    '+str)[stridx:stridx+15]
		stridx += 1
		if stridx == len(str)+4:
			stridx = 0
		cad.lcd.write(str_rotate)
	else :
		cad.lcd.write(str)
	
def musicMainfunc():
	global pin
	global remote
	global volume
	global play
	global mute
	global curSong
	global stridx
	
	curSong = 0
	pin = -1
	remote = -1
	
	mixer.init()
	mixer.music.set_volume(float(volume)/100)
	while True:
		if curSong == len(song):
			curSong = 0
		cad.lcd.clear()
		cad.lcd.blink_off()
		cad.lcd.cursor_off()
		mixer.music.load(song[curSong])
		mixer.music.play()
		writeTopLine()
		count_t = time.time()
		while mixer.music.get_busy() == True:
			writeBottomLine()
			if time.time()-count_t > 1:	# writeTopLine every 1 second
				writeTopLine()
				count_t = time.time()
			# song control
			if pin == 1 or remote == 1:	# prev song
				curSong-=2
				if curSong==-2:
					curSong=len(song)-2
				pin = -1
				remote = -1
				stridx = 0
				break;
			elif pin == 3 or remote == 3:	# next song
				pin = -1
				remote = -1
				stridx = 0
				break;
				
			elif pin == 2 or remote == 2:	# play and pause
				if play:
					mixer.music.pause()
					play = False
				else:
					mixer.music.unpause()
					play = True
				pin = -1
				remote = -1
					
			# volume control
			elif pin == 6 or remote == 6:	# volume down
				while True:
					volume -= 3
					if volume < 0:
						volume = 0
					mixer.music.set_volume(float(volume)/100)
					updateVolume();
					time.sleep(0.2)
					if not cad.switches[6].value:
						break
				pin = -1
				remote = -1
			elif pin == 7 or remote == 7:	# volume up
				while True:
					volume += 3
					if volume > 100:
						volume = 100
					mixer.music.set_volume(float(volume)/100)
					updateVolume();
					time.sleep(0.2)
					if not cad.switches[7].value:
						break
				pin = -1
				remote = -1
			elif pin == 5 or remote == 5:	# mute
				if mute:
					mixer.music.set_volume(float(volume)/100)
					mute = False
				else:
					mixer.music.set_volume(0)
					mute = True
				pin = -1
				remote = -1
			elif pin == 4 or remote == 4:	# quit
				pin = -1
				remote = -1
				mixer.quit()
				return
		curSong+=1
		
		
def radioMainFunc():	# Internet radio streaming player
	global pin
	global remote
	global volume
	global play
	global mute
	
	f = open(path,'w')
	if mute:
		f.write('volume 0 1\n')
	else:
		f.write('volume '+ str(volume) +' 1\n')
	f.close()
	
	play = True;
	curCh = 0
	cad.lcd.clear()
	writeBottomLine()
	cad.lcd.write('CH : '+channelName[curCh])

	while True:
		showTime()
		# play control
		if pin == 2 or remote == 2:		# pause and unpause
			os.system('echo \"pause\" > /tmp/mplayer-control')
			if play:
				play = False;
			else:
				play = True;
			writeBottomLine()
			pin = -1
			remote = -1
		elif pin == 3 or remote == 3:	# next channel
			curCh = (curCh + 1) % len(channelLink)
			os.system('echo \"loadfile '+channelLink[curCh] +'\" > /tmp/mplayer-control')
			cad.lcd.clear()
			writeBottomLine()
			cad.lcd.write('CH : '+channelName[curCh])
			pin = -1
			remote = -1
		elif pin == 1 or remote == 1:	# prev channel
			if curCh == 0:
				curCh = len(channelLink)-1
			else:
				curCh -= 1
			os.system('echo \"loadfile '+channelLink[curCh] +'\" > /tmp/mplayer-control')
			cad.lcd.clear()
			writeBottomLine()
			cad.lcd.write('CH : '+channelName[curCh])
			pin = -1
			remote = -1
		# volume control
		elif pin == 6 or remote == 6:	# volume down
			while True:
				volume -= 3
				if volume < 0:
					volume = 0
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				updateVolume();
				time.sleep(0.2)
				remote = -1
				if not cad.switches[6].value:
					break
			pin = -1
		elif pin == 7 or remote == 7:	# volume up
			while True:
				volume += 3
				if volume > 100 :
					volume = 100
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				updateVolume();
				time.sleep(0.2)
				remote = -1
				if not cad.switches[7].value:
					break
			pin = -1
		elif pin == 5 or remote == 5:	# mute
			if mute:
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				mute = False;
			else:
				os.system('echo \"volume 0 1\" > /tmp/mplayer-control')
				mute = True;
			updateVolume();
			pin = -1
			remote = -1
		elif pin == 4 or remote == 4: 	# quit
				pin = -1
				remote = -1
				os.system('echo \"quit\" > /tmp/mplayer-control')
				return
			

def mainFunc():
	global volume
	listener = pifacecad.SwitchEventListener(chip=cad)
	for i in range(8):
		listener.register(i, pifacecad.IODIR_FALLING_EDGE, update_pin_text)
	listener.activate()

	remote_listener = pifacecad.IREventListener(prog='myprogram')
	for i in range(8):
		remote_listener.register(str(i), print_ir_code)
	remote_listener.activate()

	radioMain = threading.Thread(target=radioMainFunc)
	while True:
		musicMainfunc()
		radioMain = threading.Thread(target=radioMainFunc)
		os.mkfifo(path)
		radioMain.start()
		os.system('mplayer -slave -input file='+path+' '+channelLink[0])
		radioMain.join()
		os.remove(path)
	
mainFunc()


1. 이전 곡 & 이전 채널        <-  KEY_CHANNELDOWN

2. 재생 및 일시정지            <-  KEY_OK

3. 다음 곡 & 다음 채널        <-  KEY_CHANNELUP

4. 음악, 라디오 모드 변경     <-  KEY_MENU

5. 음소거 및 해제               <-  KEY_MUTE

6. 볼륨 낮추기                   <-  KEY_VOLUMEUP

7. 볼륨 올리기                   <-  KEY_VOLUMEDOWN


위와 같이 수정 하였다. 아래 데모를 보고 제대로 실행되는지 확인해 보자.







+ Recent posts