2013年4月24日水曜日

Haskell に興味がある人向け xmonad 設定ガイド



2016/03/12 追記: あらためて読み返してみると,Haskell に関連する記述はだいたい間違っていることがわかりました.xmonad の設定をするうえではあまり困らないことだとは思いますが,その旨ご了承ください.

2013年4月14日日曜日

Linux で USB DAC を使う

いいヘッドフォンを買ったので、USB DAC にもチャレンジしてみた。
購入前はどの DAC が Linux で使えるかよくわからなかったので、調べたことをメモメモ。
環境は以下のとおり。
  • Linux 3.7.10-gentoo
  • ALSA
  • PulseAudio とか JACK は使ってない、よくわからない
まずどの DAC が使用可能かは、Windows や Mac の OS 標準ドライバで動くと謳っているものは (だいたい?) Linux でも使用可能なよう。
USB オーディオの接続方式として、大きく分けると以下の 3 つがある。
  • USB Audio Class 1
  • USB Audio Class 2
  • その他独自方式
USB Audio Class 1, 2 が USB.org で策定されており Linux もこれらに対応しているため、普通に ALSA のドライバで使える。
独自方式にものについては Windows や Mac にも OS 標準ドライバとして搭載できないため別途ドライバをインストールする必要があり、そうすると Linux 向けのドライバがあるものは少ないでしょう。

カーネルコンフィグで有効にするものは以下。
CONFIG_SND_USB_AUDIO=m
これでデバイスを接続するとあっさりと認識された。
$ dmesg
--- snip ---
[   53.016830] usb 3-3.7: New USB device found, idVendor=0909, idProduct=0015
[   53.016833] usb 3-3.7: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[   53.016837] usb 3-3.7: Product: USB Headphone Amp.
[   53.016840] usb 3-3.7: Manufacturer: audio-technica
[   53.017051] usb 3-3.7: usb_probe_device
[   53.017067] usb 3-3.7: configuration #1 chosen from 1 choice
[   53.017213] usb 3-3.7: Successful Endpoint Configure command
[   53.017402] usb 3-3.7: adding 3-3.7:1.0 (config #1, interface 0)
[   53.017526] usbhid 3-3.7:1.0: usb_probe_interface
[   53.017540] usbhid 3-3.7:1.0: usb_probe_interface - got id
[   53.027020] input: audio-technica USB Headphone Amp. as /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3.7/3-3.7:1.0/input/input11
[   53.027346] hid-generic 0003:0909:0015.0005: input,hidraw4: USB HID v1.00 Device [audio-technica USB Headphone Amp.] on usb-0000:00:14.0-3.7/input0
[   53.027421] usb 3-3.7: adding 3-3.7:1.1 (config #1, interface 1)
[   53.030843] usb 3-3.7: adding 3-3.7:1.3 (config #1, interface 3)
[   53.039448] snd-usb-audio 3-3.7:1.1: usb_probe_interface
[   53.039463] snd-usb-audio 3-3.7:1.1: usb_probe_interface - got id
[   53.049793] usb 3-3.7: Successful Endpoint Configure command
[   53.050427] usb 3-3.7: Successful Endpoint Configure command
[   53.050878] usb 3-3.7: Successful Endpoint Configure command
[   53.051410] usb 3-3.7: Successful Endpoint Configure command
[   53.052305] usbcore: registered new interface driver snd-usb-audio
--- snip ---
$ cat /proc/asound/cards 
 0 [PCH            ]: HDA-Intel - HDA Intel PCH
                      HDA Intel PCH at 0xa0610000 irq 47
 1 [Amp            ]: USB-Audio - USB Headphone Amp.
                      audio-technica USB Headphone Amp. at usb-0000:00:14.0-3.7, full speed
$ aplay -L
--- snip ---
sysdefault:CARD=Amp
    USB Headphone Amp., USB Audio
    Default Audio Device
front:CARD=Amp,DEV=0
    USB Headphone Amp., USB Audio
    Front speakers
surround40:CARD=Amp,DEV=0
    USB Headphone Amp., USB Audio
    4.0 Surround output to Front and Rear speakers
--- snip ---

実際に音を出すには、ALSA のデフォルトの出力先か、アプリケーション単位で出力先デバイスを設定する必要がある。前者は ~/.asoundrc に設定すれば良い。
# ~/.asoundrc
pcm.!default sysdefault:Amp
この設定はアプリケーション起動時のものが反映されるようなので、ノート PC を使用している時など接続変更が発生する時でも、接続後に設定、アプリーケション起動としてやれば良い。

USB DAC をつないで音が良くなったか? というのは別問題。耳が追いついていない。

参考

第1回 USB-DACといっても必ずしも同じではない | Gaudio+PCオーディオfan: http://www.pc-audio-fan.com/special/usb-dac-research/20101115_1438/
USB.org - Approved Device Class Document Download: http://www.usb.org/developers/devclass_docs
What's Best Forum - USB audio: http://www.whatsbestforum.com/showthread.php?2932-USB-audio
.asoundrc - ALSA wiki: http://alsa.opensrc.org/.asoundrc

2013年4月12日金曜日

QuickCheck を使う

Real World Haskell の Chapter 11 に QuickCheck の使い方が載っていて、これは素晴らしいと思ったのだけれど、一部記載が古いバージョン (QuickCheck 1) に関するものであり現行バージョン (QuickCheck 2) でそのまま使えない箇所があったため、補完的な意味で書いてみる。
動作環境は以下のとおり。

  • ghc-7.4.2
  • QuickCheck-2.4.2

1. 概要

QuickCheck はユニットテストを行うための仕組みで、テストにランダムな値を生成して与えることができる。具体的な値を決めてテストを実施するわけではないので、テスト通過可否はその性質によって判断する。
以下はクイックソートの例。クイックソート本体 (qsort) の他に、qsort の動作を確認するための関数 prop_sorted を記述している。これは qsort 後のリストが昇順にソートされているかを確認する。
-- file: QuickSort.hs
import Data.List
import Test.QuickCheck

qsort :: Ord a => [a] -> [a]
qsort []     = []
qsort (x:xs) = qsort lhs ++ [x] ++ qsort rhs
    where lhs = filter  (< x) xs
          rhs = filter (>= x) xs

prop_sorted :: Ord a => [a] -> Bool
prop_sorted xs = sorted $ qsort xs
    where sorted (x:xs@(x':_)) = x <= x' && sorted xs
          sorted _             = True
GHCi 上でテスト実施。prop_sorted に 100 通りの値を与えて qsort の動作を確認している。
ghci> :l QuickSort.hs
[1 of 1] Compiling Main             ( qsort.hs, interpreted )
Ok, modules loaded: Main.
ghci> quickCheck (prop_sorted :: [Integer] -> Bool)
+++ OK, passed 100 tests.
具体的には、こんな値を与えている (verboseCheck で確認可能)。
ghci> verboseCheck (prop_sorted :: [Integer] -> Bool)
Passed:  
[]
Passed: 
[]
Passed:  
[0,-2]
Passed:  
[0,3,0]
Passed:  
[-3,-2,-3,4]
--- snip ---
Passed:   
[-29,68,-24,-85,-7,-76,27,34,65,-31,-62,-27,65,-53,76,-82,24,97,-43,-70,-67,58,
56,-95,-48,18,26,-91,97,-56,33,10,-57,29,-61,-60,-23,-87,77,-9,-30,21,-89,-32,
-42,-73,21,60,65,-9,-76]
Passed:   
[97,-9,-27,42,-30,49,-97,46,-48,11,-87,-47,-36,45,-55,-25,2,-58,-43,82,3,81,97,
-28,-84,75,-13,42,-50,0,7,54,46,-13,4,73,-35,-32,-77,-37,-42,81,-3]
Passed:   
[-78,85,3,18,-67,94,99,-17,-66,94,-1,-83,-96,-68,-31,72,-79,81,47,24,-78,20,-87,
-56,38,65,-69,40,-54,-53,42,96,15,62,55,-10,-89,-83,67,36,-85,-97,71,-28,-3,77,
84,-7,57,-55,11,84,-85,-89]
+++ OK, passed 100 tests.
ランダムテストの良いところは、人間が思いもよらない値をテストに与えることができること、具体的な値を定義する手間が省けること。そもそもコードとテストを書く人間が同じ場合、テストパターンとして思い浮かぶ時点で、コードにもその考慮がされていることが多いんじゃないでしょうか。

2. 値の生成

値の生成は System.Random で乱数を発生させて、それに基づき Arbitrary クラスで定義された arbitrary 関数で当該型の値を生成するという流れになる。なお、Gen モジュールでランダムな値を生成するための便利な関数が用意されているため、arbitrary 関数で直接 System.Random の乱数を扱う必要は多分ほとんどない。以下がキーとなる (と RWH に書かれていた) 関数。
-- module: Test.QuickCheck.Gen
elements :: [a] -> Gen a
choose :: Random a => (a, a) -> Gen a
oneof :: [Gen a] -> Gen a
なお、Gen は state-passing monad ということだがモナドはまだよく理解できていない。とにかく、実際に GHCi 上で値を取り出して挙動を確認するためには sample 関数を使用する。
-- module: Test.QuickCheck.Gen
sample :: Show a => Gen a -> IO ()
GHCi 上でテスト。
ghci> :m +Test.QuickCheck
ghci> sample $ elements [0,1,2,3,5,8]
8
3
1
8
0
5
5
2
3
2
8
ghci> sample $ choose ('a','z')
'h'
'f'
'g'
'q'
'c'
'i'
'a'
'a'
'a'
'r'
'g'
ghci> sample $ oneof [choose (0,10), choose (100,110), choose (200,210)]
207
0
9
207
105
8
106
202
8
202
110
これらを使用して、必要な型の乱数値を生成する。例えば、あらかじめ実装されている Char は以下のようになっている。
-- module: Test.QuickCheck.Arbitrary
instance Arbitrary Char where
  arbitrary = chr `fmap` oneof [choose (0,127), choose (0,255)]
これを使用して独自の型の乱数値を生成する例。良い例が思い浮かばなかったので Real World Haskell の例そのまま。
-- Doc.hs
import Test.QuickCheck  
import Control.Monad (liftM, liftM2)  
  
data Doc = Empty  
         | Char Char  
         | Text String  
         | Line  
         | Concat Doc Doc  
         | Union Doc Doc  
         deriving (Show)  
  
instance Arbitrary Doc where  
    arbitrary =  
        oneof [ return Empty  
              , liftM Char arbitrary  
              , liftM Text arbitrary  
              , return Line  
              , liftM2 Concat arbitrary arbitrary  
              , liftM2 Union arbitrary arbitrary ]  
arbitrary は型推論され、関数に必要な型の乱数値を与えている。Text には String を、Concat には再帰して Doc の値を与える。
ghci> :t arbitrary
arbitrary :: (Arbitrary a) => Gen a
ghci> :m +Control.Monad
ghci> :t liftM Text
liftM Text :: (Monad m) => m String -> m Doc
ghci> :t liftM2 Concat
liftM2 Concat :: (Monad m) => m Doc -> m Doc -> m Doc
また、arbitrary 関数中の oneof .. の部分は以下のように書くこともできるが、oneof ... のほうがすっきりしてわかりやすい。
instance Arbitrary Doc where
    arbitrary = do
        n <- choose (1, 6) :: Gen Int
        case n of
             1 -> return Empty

             2 -> do x <- arbitrary
                     return (Char x)
--- snip ---
sample 関数で生成を確認する。
ghci> :l Doc.hs
[1 of 1] Compiling Main             ( Doc.hs, interpreted )
Ok, modules loaded: Main.
ghci> sample (arbitrary :: Gen Doc)  
Char 'r'  
Line  
Union (Text "Ce") (Char '\194')  
Text "\179lj(\169K"  
Text "\233y\189d"  
Union (Union (Char 'K') (Concat (Concat Empty (Char 'c')) Empty)) (Char 'A')  
Text "\213V\236s\ESC\167\NUL\224\ENQ4u"  
Union (Union Empty (Char '\219')) (Char '\138')  
Line  
Union Line (Concat Empty (Concat (Char 'w') Line))  
Char '}'  

3. テストをまとめて実行する

テストの作り方は上に示したが、テストを複数作ってそれをひとつずつ GHCi で実行していくわけにはもちろんいかないので、まとめる。関数の中にひとつずつ quickCheck prop_hoge と記述していっても良いが、全部のテストをまとめて行うための仕組みも用意されている。{-# LANGUAGE TemplateHaskell #-} したうえで $quickCheckAll を実行するだけ。
-- TestSuite.hs
{-# LANGUAGE TemplateHaskell #-}

import Test.QuickCheck
import Test.QuickCheck.All

prop_plus1 :: Integer -> Bool
prop_plus1 x = x + 1 > x

prop_abs :: Integer -> Bool
prop_abs x = abs x >= 0

prop_square :: Integer -> Bool
prop_square x = x ^ 2 > x

main = $quickCheckAll
これで全てのテストが実行される。prop_square は失敗するテストの例。
$ runghc TestSuite.hs 
=== prop_plus1 on TestSuite.hs:6 ===
+++ OK, passed 100 tests.
=== prop_abs on TestSuite.hs:9 ===
+++ OK, passed 100 tests.
=== prop_square on TestSuite.hs:12 ===
*** Failed! Falsifiable (after 1 test):  
0
False
一番大事なことは、テストを書くことをめんどくさがらないことですかね。

参考

Real World Haskell Chapter 11. Testing and quality assurance: http://book.realworldhaskell.org/read/testing-and-quality-assurance.html
Introduction to QuickCheck2: http://www.haskell.org/haskellwiki/Introduction_to_QuickCheck2