2008年12月11日

來自 Sir Paul 的音樂 - 不含 DRM!

阿怪曾經對我說:「從哪裡跌倒,就從哪裡站起來。」他指的是唱片工業在網路上大跌一跤,所以他發憤一定要學會寫程式:既然無法打敗網路,就加入網路吧!

唱片工業面對網路的對策一向是 DRM - Digital Rights Management,也就是在音樂 CD 上或數位檔案裡埋入機關,讓買了音樂的人不能任意拷貝、甚至限制能播放該音樂檔的設備或次數。Apple 的 iPod + iTunes + iTunes store 之所以可以得到那些大唱片公司授權在網路上賣音樂,一般相信 Apple 的 DRM 技術是關鍵因素。

DRM 技術不便宜,而且是個道魔不斷比高的遊戲,不是每個唱片出版公司玩得起的。

近年來聽說也有些人想通了,既然玩不起 DRM 或是 DRM 無效,乾脆就閉著眼睛放任拷貝,當作成名或宣傳的管道,只要紅了,就不難從演唱會賺回來。不過已成名的歌手或團體通常不願意這麼做。

要說名氣,在流行音樂界沒幾個人能和 Sir Paul McCartney 相提並論的。他從 Beatles 時代和 John Lennon 就是 Beatles 的兩大支柱,雖然絕大部分的曲子都寫是 John 和 Paul 合寫的,但聰耳人都聽得出來哪首是 John 寫的,哪首是 Paul 寫的。後來他在 1997 年受英國女王封為 Knight,成了 Sir Paul McCartney。

Sir Paul 以 The Fireman 這個兩人團體為名發表的新專輯 Electric Arguments,竟然是完全無 DRM 的音樂!而且銷售手法真的有意思:

  • 可以 $8.99 買無失真 MP3
  • 可以 $12.99 買 CD 送無失真 MP3
  • 厲害的是,還可以 $29.99 元買黑膠唱片送 CD 和無失真 MP3!對!你沒看錯!黑膠吔!
  • 更神奇的是,在 2009/1/31 之前可以 $79.99 購買限量豪華版的唱片,內含以上所有東西和可以自己 remix 的多軌音檔。
  • 想先試聽?可以直接上 The Fireman 官網聽,免費!而且不是選過的少數幾首的片段喔,是全部 13 首完整呈現!
這樣才對嘛!只要是我喜歡的音樂,要我花點錢當然沒問題!最討厭的就是唱片灌水的行為,整張唱片只有一兩首好聽、三四首能聽,也不一定有試聽的機會(站在唱片行一整張試聽完會被瞪白眼吧?),卻要我花整張的錢。Sir Paul 的作法,讓我真的是聽到喜歡了再買,這一格也是邊聽 Electric Arguments 邊刻出來的。

不管你買哪一種,全部都沒有 DRM,愛怎麼拷就怎麼拷,沒有技術上的限制(拷給別人還是有著作權的問題喔,請自重),可以電腦裡放一份,iPod 裡放一份,手機裡放一份,再燒張 MP3 車上放一份、床頭音響放一份,只要是自己聽,通通沒問題!

但反過來說,這也是試水溫,測試聽眾的行為... 可以拷貝,你還會不會花錢買呢?可以 $8.99 買到 MP3,你會不會買 $12.99 的 CD 呢?我認為 Sir Paul 的老歌迷們,尤其是和他一樣已經當阿公阿嬤的,應該會懶得省這幾塊錢自己花力氣拷貝,直接買 CD 放進 CD 唱機比較簡單,何況還有個美美的 CD 封面可以欣賞。黑膠就更不用說了,有幾個人能自己拷一張黑膠啊?一定是老歌迷直接買回來收藏的。

聆聽這張專輯的成本,從 $0 到 $79.99,你要花多少?到頭來這張專輯會賣出幾張呢?製作人賺得回製作專輯和架網站的成本嗎?這對未來唱片工業的行銷方式,有多大的影響呢?

2008年12月4日

Python 3.0 的新玩意(第一部)

真可說是「萬眾囑目」的 Python 3.0 終於在 2008/12/3 釋出了!有興趣了解 Python 3.0 和 Python 2.x 有什麼不同的人可以去讀 GvR 寫的 "What’s New In Python 3.0",這篇是我讀文的筆記。

先記一下,舊的 Python script 可以用 2to3 來轉成 Python 3.0 的 script,可是不見得 100% 轉得完整。

print 從敍述(statement)降級為函式(function)

我的愛好度:heartheart

print "x =", x 這樣的敍述吻別吧!從此要寫 print("x =", x) 了,不過多了一些 keyword arguments 可以修改 print() 的行為,如 sep= 指定字串之間的分隔字,end= 指定印完要加的字(取代預設的換行字元)和 file= 指定要輸出的檔案物件。

我知道 print 沒必要是個敍述,用函式就可以做得很好,又容易自己換掉(__builtins__.print = 新函式就好了),但是為了印個東西打一堆括弧實在很煩,更何況大部分是為了 debug!這時候就想起 iPython 的好處了...

大量用 iterator 取代 list

我的愛好度:heartheartheartheartheart

早就該這麼做了!從此在大多數情形都能用 iterator,在處理大的 list 時就不用擔心會在記憶體多生一個大 list 跑不動了。

xrange()dict.iteritems() 都是極度礙眼的東東,從此完全消失!range()dict.items() 現在傳回的就是 iterator 了。

map()filter() 也傳回 iterator,直接用在迴圈裡又快又省。

比大小

我的愛好度:heartheartheartheart

我常用的 cmp() 和較少用的 __cmp__() 都消失啦!那要對複雜物件組成的清單(list)排序的時候怎麼辦?

看來要愛用 key= 和 operator 模組裡的 itemgetter()attrgetter() 兩個函式了。key= 指定一個函式,在排序時會把清單中的項目一一傳入,函式要傳回用來比較的資料。舉例來說,如果有個 list 是 aList = [ (5,'a'), (3,'z'), (7,'h'), (9,'o') ],要依項目一號排序就這樣寫:
  sorted(aList, key=operator.itemgetter(1))
結果會是
  [(3, 'a'), (1, 'h'), (9, 'o'), (5, 'z')]
如果有個 class Foo 是這樣:
class Foo:
def __init__(self, x, y): self.x, self.y = x, y
def __repr__(self): return "(%s,%s)" % (self.x,self.y)
要把 bList = [Foo(3,'a'), Foo(9,'o'), Foo(5,'o'), Foo(1,'h')] 依 y 屬性排序就這樣寫:
  sorted(bList, key=operator.attrgetter('y')) # 結果 [(3,a), (1,h), (9,o), (5,o)]
要先依 y 排序、再依 x 排序的話:
  sorted(bList, key=operator.attrgetter('y','x')) # 結果 [(3,a), (1,h), (5,o), (9,o)]

整數變大了

我的愛好度:heartheartheart

不用再管 long 和 int 的差別了,不用再見到數字後面那個沒必要的 L 了,因為所有的整數都是無限位數了!(喔,當然記憶體容量是個限制。)好棒啊!

不過 3/5 變成浮點除法 = 0.6,3//5 才是整數除法 = 0。完全記不得以前寫的程式那裡用到整數除法了啊!2to3 不但不會轉除法,連警告都沒有。 Orz

還有八進位整數的表示法也從 0127 變成 0o127(零+英文字母 o + 數字),還好 2to3 抓得到。

字串變簡單了

我的愛好度:heartheartheartheart

以前寫 Python 2.x 的 script 常常在處理中文的時候被文字編碼和解碼搞得昏頭,記不得哪個變數是 unicode、哪個變數是 str,尤其是當資料是從檔案讀進來的時候,然後一個簡單的 c = a + b 都可以因為 a 是 str、b 是 unicode 而吐個很難懂的
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
錯誤訊息。喂!我明明在 script 檔頭指定了 # -*- coding: utf8 -*- 你還搞 ASCII codec?

在 Python 2.x 中直接用引號夾起來的是 str,內容是用 UTF8、Big5、Shift-JIS 等某個編碼系統編好的字串,想建立 unicode 物件的話,要用 str.decode() 來解碼,或是用 u"..." 來夾住文字。
  a = "中文"; b = u"字串"; c = "測試".decode("utf8")
print type(a), a, type(b), b, type(c), c # <type 'str'> 中文 <type 'unicode'> 測試 <type 'unicode'> 測試
這裡 c 是從 string literal decode 得到的 unicode 物件。

到 Python 3.0 的時代,所有 str type 的字串都內建以 unicode 儲存,不再另有 unicode 這個 type,而編碼過的字串或 binary 的資料則用新的 bytes type 儲存。從 str 到 bytes 是 encode,從 bytes 到 str 是 decode。
  a = "中文"; b = u"字串"; c = "測試".encode("utf8")  # b 會有錯誤訊息,因為已經沒必要用 u"..." 語法了
print(type(a), a, type(c), c) # <class 'str'> 中文 <class 'bytes'> b'\xe6\xb8\xac\xe8\xa9\xa6'
這裡 c 是從 str literal 編碼而得的 UTF8 內容,在 Python 3.0 裡是個 binary 的 bytes 物件。

換句話說,不再有字串和 unicode 字串兩種字串了,字串一律用 unicode 表示,非 unicode 的資料則是 bytes,可以用 b"..." 表示。

在 Python 3.0 裡把 str 和 bytes 加起來會得到比較好懂的錯誤訊息:
  TypeError: Can't convert 'bytes' object to str implicitly
開檔也不一樣了。open(filename) 是用 text mode 開啟,所以一定會用某種編碼去解釋讀到的資料,轉成 str 放在記憶體裡(記住,str 是 unicode),預設的編碼會從環境變數 LANG 決定,但可以用 open(filename, encoding="...") 指定編碼系統。如果不想把檔案裡的資料轉成 unicode,或者資料根本不是文字而是 binary data,就可以用 open(filename, "b") 來開檔。用 "b" (binary mode) 開檔讀 bytes data 的確比較容易理解。

非英文檔名也有支援,不過和作業系統有關。

辨識名可以用 UTF8 字元

我的愛好度:heartheartheartheart

變數名、函式名、class 名都可以用中文了!
  彤彤生日 = datetime.date(1997, 7, 5); print(彤彤生日)
這樣子周蟒的功能有一部分就直接被 Python 3.0 實現,更方便了。


(待續)