2016年6月28日 星期二

遙控電腦的 python code

========= homeirctl.py =============================

from ctypes import *
from array import array
from struct import pack

import pyaudio
import codeset
import subprocess
import signal, sys

THRESHOLD = 2000
CHUNK_SIZE = 1024
FORMAT = pyaudio.paInt16
RATE = 38000

ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)
def py_error_handler(filename, line, function, err, fmt):
  pass

def record():
    """
    Record a word or words from the microphone and
    return the data as an array of signed shorts.

    """
    p = pyaudio.PyAudio()
    stream = p.open(format=FORMAT, channels=2, rate=RATE,
        input=True, output=True,
        frames_per_buffer=CHUNK_SIZE)

    num_silent = 0
    rcv_started = False

    r = array('h')

    while 1:
        rcv_data = array('h', stream.read(CHUNK_SIZE))
        rcv_data = [ (x>THRESHOLD) for x in rcv_data ]
        silent = (sum(rcv_data) < 10 )

        if silent and rcv_started:
          num_silent += 1
        elif not silent and not rcv_started:
          rcv_started = True

        if rcv_started :
          r.extend(rcv_data)

        if rcv_started and num_silent > 2:
          break

    stream.stop_stream()
    stream.close()
    p.terminate()
    return r

def to_bit(data) :
    sumData = []
    pN = 0
    nN = 0
    started = False
    for j in data :
      if j :
        pN = pN + 1
        if pN > 10 :
          started = True
        if nN < -10 :
          sumData.append(nN)
          nN = 0
      elif started :
        nN = nN - 1
        if pN > 10 :
          sumData.append(pN)
          pN = 0
    if pN > 10 :
      sumData.append(pN)
    if started and (nN < -10) :
      sumData.append(nN)

    result = ''
    started = False

    it =  iter(sumData)
    for x1 in it :
      try:
        x = x1 + next(it)
      except:
        break

      if x1>120 :
        result = ''
      elif x < -300 or len(result)>50:
        break
      elif x < -90:
        result = result + '1'
      else:
        result = result + '0'

    return result

def sigterm_handler(_signo, _stack_frame):
    sys.exit(0)

def main() :

  while True:
    data = record()
    signal = to_bit(data)
    try:
      buttonName = codeset.TVCODE[signal]
      print(buttonName)
    except:
      print(signal,'key not found')
    else:
      if buttonName is 'red' :
        subprocess.call("/home/viola/autoplay/starttv.sh", shell=True)
      elif buttonName is 'green' :
        subprocess.call("/home/viola/autoplay/starttvnews2.sh", shell=True)
      elif buttonName is 'yellow' :
        subprocess.call("/home/viola/autoplay/starttvnews3.sh", shell=True)
      elif buttonName is 'blue' :
        subprocess.call("/home/viola/autoplay/starttvnews1.sh", shell=True)
      elif buttonName is 'stop' :
        subprocess.call("/home/viola/autoplay/stopall.sh", shell=True)
        subprocess.call('xrandr --output VGA1 --mode 1440x900 --output HDMI1 --off', shell=True)
      elif buttonName is 'option' :
        subprocess.call("/home/viola/autoplay/startmusic.sh", shell=True)
      elif buttonName is 'soundtrack' :
        subprocess.call('xdotool search --name " - mpv" key s', shell=True)
      elif buttonName is 'soundpattern' :
        subprocess.call('xdotool search --name " - mpv" key A', shell=True)
      elif buttonName is 'speedup' :
        subprocess.call('xdotool search --name " - mpv" key Right', shell=True)
      elif buttonName is 'speeddown' :
        subprocess.call('xdotool search --name " - mpv" key Left', shell=True)
      elif buttonName is 'pauseplay' :
        subprocess.call('xdotool search --name " - mpv" key space', shell=True)
      elif buttonName is 'stepf' :
        subprocess.call('xdotool search --name " - mpv" key n', shell=True)
      elif buttonName is 'stepb' :
        subprocess.call('xdotool search --name " - mpv" key b', shell=True)
      elif buttonName is 'vup' :
        subprocess.call('xdotool search --name "playing music" key bracketright', shell=True)
      elif buttonName is 'vdown' :
        subprocess.call('xdotool search --name "playing music" key bracketleft', shell=True)
      elif buttonName is 'cursorU' :
        subprocess.call('xdotool search --name "playing music" key N', shell=True)
      elif buttonName is 'cursorD' :
        subprocess.call('xdotool search --name "playing music" key B', shell=True)
      elif buttonName is 'cursorL' :
        subprocess.call('xdotool key XF86AudioLowerVolume', shell=True)
      elif buttonName is 'cursorR' :
        subprocess.call('xdotool key XF86AudioRaiseVolume', shell=True)
      elif buttonName is 'mute' :
        subprocess.call('xdotool key XF86AudioMute', shell=True)
      elif buttonName is 'epg' :
        subprocess.call('chromium-browser --app="http://hichannel.hinet.net/radio/mobile/index.do?id=205#"', shell=True)
      elif buttonName is '3D' :
        subprocess.call("/home/viola/autoplay/startplaying.sh", shell=True)
      elif buttonName is 'information' :
        subprocess.call('xdotool search --name " - mpv" key t', shell=True)
      elif len(buttonName)==1 :
        subprocess.call('xdotool search --name " - mpv" key '+buttonName, shell=True)

if __name__ == '__main__':
  signal.signal(signal.SIGTERM, sigterm_handler)
  signal.signal(signal.SIGINT, sigterm_handler)

  c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
  asound = cdll.LoadLibrary('libasound.so')
  asound.snd_lib_error_set_handler(c_error_handler)

  main()

========== codeset.py ====================
TVCODE = {
'111111110001000000001110':'red',
'111111011101000000100010':'green',
'111111000111000000111000':'yellow',
'111111100111000000011000':'blue',
'111111111010000000000101':'pauseplay',
'111111101110000000010001':'speedup',
'111111010110000000101001':'speeddown',
'111111100011000000011100':'stop',
'101101110010010100001101':'stepf',
'101101010010010100101101':'stepb',
'111111010010000000101101':'option',
'111111011000000000100111':'vup',
'111111111000000000000111':'vdown',
'111111100000000000011111':'power',
'111111000110000000111001':'soundtrack',
'111111100110000000011001':'soundpattern',
'111111101000000000010111':'mute',
'111111110000000000001111':'information',
'111111010111000000101000':'epg',
'111111011001000000100110':'return',
'111111100101000000011010':'OK',
'101101101110010100010001':'3D',
'111111101101000000010010':'cursorR',
'111111001101000000110010':'cursorL',
'111111110101000000001010':'cursorU',
'111111010101000000101010':'cursorD',
'111111000100000000111011':'1',
'111111100100000000011011':'2',
'111111100100000000011011':'3',
'111111010100000000101011':'4',
'111111000100000000111011':'1',
'111111100100000000011011':'2',
'111111010100000000101011':'3',
'111111110100000000001011':'4',
'111111001100000000110011':'5',
'111111101100000000010011':'6',
'111111011100000000100011':'7',
'111111111100000000000011':'8',
'111111000010000000111101':'9',
'111111100010000000011101':'0' }

============= recordWavToFile.py =====================
import pyaudio
import wave
import numpy
from array import array

FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 38000
CHUNK = 1024
RECORD_SECONDS = 2
WAVE_OUTPUT_FILENAME = "file.wav"

audio = pyaudio.PyAudio()

# start Recording
stream = audio.open(format=FORMAT, channels=CHANNELS,
                rate=RATE, input=True,
                frames_per_buffer=CHUNK)
print "recording..."
frames = []

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)
print "finished recording"


# stop Recording
stream.stop_stream()
stream.close()
audio.terminate()

waveFile = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(frames))
waveFile.close()

================ drawWavPlot.py ====================
from scipy.io import wavfile
from matplotlib import pyplot as plt
import numpy as np

# Load the data and calculate the time of each sample
samplerate, data = wavfile.read('file.wav')
print data
times = np.arange(len(data))/float(samplerate)

# Make the plot
# You can tweak the figsize (width, height) in inches
plt.figure(figsize=(20, 4))
plt.fill_between(times, data[:,0], data[:,1])
plt.xlim(times[0], times[-1])
plt.xlabel('time (s)')
plt.ylabel('amplitude')
#You can set the format by changing the extension
#like .pdf, .svg, .eps
plt.savefig('plot.png', dpi=100)
plt.show()

DIY 用電視遙控器控制電腦

簡單說, 要DIY的硬體就長這樣 :


左邊是音源線的接頭--舊音源線剪下來剝了外皮的, 右邊是一顆紅外線接收器 VS1838B. (別的紅外線接收器能不能用我不知道, 我也是在網路上看來的, 然後去電子材料行說要買紅外線接收器, 店員拿了三顆給我看, 我挑出和網路上(看得懂的)照片一樣的那一顆.

然後根據下面這張網路找來的圖, 把三支腳連接好
(圖是 google 來的, 沒有記下出處, 原圖主人有意見的話, 我立刻處理!)


圖裏是用三根塑膠繩分出來細絲綁的(就去買小吃時綁塑膠袋的那種繩子), 至於為什麼是這種繩子, 不為什麼, 之前是用膠帶黏的, 掉了兩次, 第三次修理時剛買了晚餐回來, 順手用上, 結果效果很好!

做好之後, 就插入電腦的麥克風接口.

開啟錄音軟體, 拿著電視遙控器對它按幾下, 能收到音的話, 這硬體就算成功了.

然後, 需要能解析變成音訊的紅外線訊號的程式, mobile01 上有人分享過 windows 上的作法, 但我家電腦是 ubuntu, 不合用.
又找了幾種做法, 都試不出來, 然後我才想到, 我不需要'正確的'解析訊號, 我只要能分辨出各個按鍵的不同就好了啊! 我自己就是 programmer, 雖然行業不同, 但應該不難吧!

1. 先把錄下的音訊畫出波型, 看有沒有什麼特徵
   -> 這個 google 到一支 python 小程式. 很快就畫出來, 然後, 果然有特徵.
2. 找個能讀音訊檔的程式, ---> 其實和上面那支同出一源
    雖然我的 python 功力就只有幼稚園級, 但好在有 google, 一個假日, 完成!
3. 聲寶電視遙控器上有一堆平常用不到的鍵, 例如一排紅黃藍綠, 好像是給外接 USB 用的, 我沒接, 所以就拿來用啦, 如藍鍵就是看東森網路新聞 :
mpv --audio-device='alsa/hdmi:CARD=Intel,DEV=0' -fs -geometry 1400:0 --ytdl-format=95 "https://www.youtube.com/watch?v=jMN4cxyhJjk"
( 我的電腦接雙銀幕, 電視定義為右邊那個, 所以用 geometry 1400:0 送到電視上去看)
4. 完成, 現在用一個電視遙控器, 就可以開電視, '轉'到東森新聞/Skynew/日本網路新聞, 好像真的有第四台似的...