顯示具有 程式 標籤的文章。 顯示所有文章
顯示具有 程式 標籤的文章。 顯示所有文章

JavaScript 函式筆記

喔要做法令易讀器於是翻了JavaScript的文章,筆記一些如下: ......
... read more

如果AI不可殺,那麼開發過程的測試品呢?

神魔之塔

我沒在玩這個,不過剛好前幾天想到AI的人道議題。
起因是想到「一個上市或供免費下載的程式,在推出之前,被編譯過多少次、又被刪掉了多少個不同版本的可執行檔」。
......
... read more

法令函文易讀器

目標

  • 讓沒有學過法律的人,也能夠在閱讀提到法律的文章時,能夠快速地查詢指定法規或條文的內容。
  • 讓有學過法律的人,能夠方便地向沒有學過的人引出實際的法條與相關解釋、判例、裁判、決議。
......
... read more

魯班尺

從長度判吉凶
長度:公分 (或使用Google進行單位換算)
結果:
雖然魯班尺的吉凶論定一般是指家宅、傢俱之用,不過既然傳藝中心舉人宅正門牆上也有相同的東西,我想就不用那麼計較了,人生也不是用長度定論的。(註:華人用的捲尺除了魯班尺的吉凶論定之外,也有量陰宅、祖先牌位之用的丁蘭尺,詳見參考資料
個人小意見:在中文中多以「陰」字開頭的性器官應該不適合用魯班尺的吉凶判斷標準。......
... read more

用SQL求得資料順位

原本是想要看看PHP Plurk API,在亂點roga部落格的MySQL分類時看到了roga大在去年六月有在想SQL的問題,於是心血來潮地試了一下。

(卻花了我兩個多小時,而這篇文不知道又要打多久了XD)

在仔細寫本文之前,先把結論呈現一下:DELETE FROM tbl
USING tbl INNER JOIN (
SELECT id, count(*) AS place
FROM (
SELECT t1.id
FROM tbl AS t1, tbl AS t2
WHERE t1.url_id = t2.url_id
AND t1.id <= t2.id
) AS t3
GROUP BY id
) AS t4
WHERE tbl.id = t4.id
AND t4.place > 1500
其中tbl是roga大大的url_detail_history,單純是名字太長所以被我換掉。此法受限於資料庫系統對Multiple-table DELETE的支援度,已知MySQL支援。


以下詳述我的思考過程(有些是繞圈子,所以看起來可能跟結果沒啥關係)
roga原本的需求大致上是「資料表中url_id欄位相同的,只留下最新的1500筆」。而該資料表有一個`id`主鍵欄位是AUTO_INCREMENT。
我試著將問題從最簡單的模式開始推導:

第一步:求「指定url_id的最新資料」

這很直覺,使用Aggregate Function中的MAX()就可以了:SELECT MAX(id) FROM tbl WHERE url_id = 'XXXX'

第二步:求「各url_id最新的一筆」

也是基本題,加上GROUP BY就可以了:SELECT url_id, COUNT(*)
FROM tbl
GROUP BY url_id
雖然沒什麼困難,不過我們稍比較一下這個和前一個SQL後會發現:
  1. WHERE變成GROUP BY
  2. SELECT區塊也要多一欄url_id,也就是GROUP BY用的欄位


第三步:求「各url_id最新的n筆」

咦....慘了,沒頭緒,也許我們第二步不應該這樣走。回到第一步,把問題改成「指定url_id的最新n筆」。(注意到題目的不同嗎?我一次只變動題目的一部分,嘗試在這樣的步驟中統整出比較有系統的解法。)

新的第二步:求「指定url_id的最新n筆」

嗯....如果不用MAX()的話,確實不難做到:SELECT id
FROM tbl
WHERE url_id = 'XXXX'
ORDER BY id DESC
LIMIT n


還是第三步:求「各url_id最新的n筆」

....我還是不知道要怎麼做

我卡了一個多小時,直到想起多年前在網路上看到的某個題目:「用SQL將名次排序出來」,雖然已經忘了網址在哪,但是解法卻非常的有趣。(效率如何我就不清楚了):
假設資料表ss的資料如下:+---------+-------+
| student | score |
+---------+-------+
| 1 | 7 |
| 2 | 5 |
| 4 | 3 |
| 5 | 4 |
+---------+-------+
而我們要產出的結果為:+---------+-------+-------+
| student | score | place |
+---------+-------+-------+
| 1 | 7 | 1 |
| 2 | 5 | 2 |
| 4 | 3 | 4 |
| 5 | 4 | 3 |
+---------+-------+-------+
也就是學生1是第一名;學生5是第三名;...

試重新思考「名次」的定義。我們直覺上對於此詞彙的解釋是:「分數第n高者為第n名」,但這個作法則是將「第n名」界定為「有n個人(含自己)的分數跟自己相同或較高」。
依照這個想法,要排序名次,資料表就得跟自身比較。讓我們試寫「列出分數不比自己低的人」的指令,也就是:SELECT t1.student, t2.student
FROM ss AS t1, ss AS t2
WHERE t1.score <= t2.score
結果為:+------------+------------+
| t1.student | t2.student |
+------------+------------+
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
| 4 | 1 |
| 4 | 2 |
| 4 | 4 |
| 4 | 5 |
| 5 | 1 |
| 5 | 2 |
| 5 | 4 |
+------------+------------+

但其實我們並不需要知道「哪些人」分數不比自己低,而是要知道有「幾個人」,所以即使沒有顯示t2.student也沒有關係:SELECT t1.student
FROM ss AS t1, ss AS t2
WHERE t1.score <= t2.score
假設這張表叫做t3,那麼「列出分數不比自己低的人的總數」,SQL指令就是:SELECT student, COUNT(*)
FROM t3
GROUP BY student
(有沒有覺得最後的指令簡潔到令人吐血?)
當然啦,通常你不需要另存t3,而會把整串步驟寫成一個指令:SELECT student, COUNT(*) AS place
FROM (
SELECT t1.student
FROM ss AS t1, ss AS t2
WHERE t1.score <= t2.score
) t3
GROUP BY student
得出來的就會是名次列表

而如果資料表裡面的主鍵並不是單一欄位組成,比方說:+---------+---------+-------+
| student | subject | score |
+---------+---------+-------+
其中Primary Key為(student, subject),那麼題目就變成「各學生在各科目的排名」,SQL指令為:SELECT student, subject, COUNT(*)
FROM (
SELECT t1.student, t1.subject
FROM ss AS t1, ss AS t2
WHERE t1.score <= t2.score
AND t1.subject = t2.subject
) t3
GROUP BY student, subject


上述的作法遇到考試科目同分時會有些狀況,不過由於今天我只想針對roga大的問題,也就是對primary key做排序,所以就不管了。

根據以上的作法,我們就可以輕易得知每一筆資料的排序順位,也就可以很輕易的執行相關的操作。而像是「列出各科目中考最差的五名」這樣的問題,即使每個科目的修課人數不同,也只要將「名次」的定義改成「分數不比自己高的人數」就可以輕易做到了。
回歸正題,roga大的狀況就會變成:DELETE FROM tbl
USING tbl INNER JOIN (
SELECT id, count(*) AS place
FROM (
SELECT t1.id
FROM tbl AS t1, tbl AS t2
WHERE t1.url_id = t2.url_id
AND t1.id <= t2.id
) AS t3
GROUP BY id
) AS t4
WHERE tbl.id = t4.id
AND t4.place > 1500
至於哪邊用大於,哪邊用小於,可得想清楚了....

(編輯終了,本文花了我一個半小時)
......
... read more

PHP + MySQL簡易操作教學

前置作業:


流程:
  1. 連線到資料庫
  2. 執行SELECT SQL
  3. 取出一列列的資料
  4. 其他注意事項和小撇步

PHP的MySQL相關function操作其實不難,但是新手們一開始在使用時卻會很不習慣,以下僅以在下的認知做出簡易示範(應該不是最好的方法,事實上我自己目前已經不是這樣做,但我相信本文應該會是很好理解的流程)
連線到資料庫

連線到資料庫通常都很好解決(如果MySQL Server有設定好的話):mysql_connect('localhost', 'username', 'password');
mysql_select_db('myDB');

這段程式碼通常會寫在一個給所有頁面引入的檔,假設上面兩行被我們存檔為"database.php",那麼遇到需要對資料庫連線的時候只要PHP呼叫:require_once 'database.php';就可以了。這個方法也可以應用在其他想要在每個頁面上做的事情(像是紀錄IP、檢查是否已登入之類的)。

不過只是這樣子有時會有點風險,比方說如果資料庫不是自己設定的,但我們又必須指定編碼(目前的趨勢是使用UTF-8)的話,就得加上一句"SET NAMES 'UTF8'"的SQL,以防止一些亂碼的狀況發生,當然這個前提是你的網頁也是用UTF-8編碼。連線的設定可能就會變成: $db_server = 'db.mycom.com';
$db_user = 'kong0107';
$db_password = '********';
$db_name = 'test';

mysql_connect($db_server, $db_user, $db_password);
mysql_select_db($db_name);
mysql_query("SET NAMES 'UTF8'");
你可能會覺得先把帳號密碼寫在變數裡這件是有點多餘,不過實務面上我們很有可能還會把「帳號資料」和「連線」這兩個區塊分開--通常是做很大的站台的時候。比方說我可能某個頁面同時要跟Google、Yahoo!和FaceBook做連線,那麼我就會分開寫兩個檔:/* 這個檔叫做config.php */
$db_server = 'db.mycom.com';
$db_user = 'kong0107';
$db_password = '********';
$db_name = 'test';

$google_user = 'kong0107';
$google_password = 'hahaha';

$yahoo_user = 'kong_crazykid';
$yahoo_password = 'oh my god';

$fb_user = 'kong0107@gmail.com';
$fb_password = 'this is longer';

/* 這個檔叫做service_connection.php */
require 'config.php'; /*就會把上面那個檔案叫進來*/

mysql_connect($db_server, $db_user, $db_password);
mysql_select_db($db_name);
mysql_query("SET NAMES 'UTF8'");

require_once 'google_api.php'; /*把google的API(最重要的是連線function)叫進來*/
$google = new GoogleAPI();
$google->connect($google_user, $google_password);

require_once 'yahoo_api.php';
$yahoo = new yahooAPI();
$yahoo->connect($yahoo_user, $yahoo_password);

require_once 'facebook_api.php';
$fb = new facebookAPI();
$fb->connect($fb_user, $fb_password);
看吧,只是連線而已就已經開始讓人頭昏眼花,而且我們根本還沒有開始真正操作什麼呢!所以還是把一些設定(諸如帳號、密碼)通通寫在同一區,這樣子之後要改才比較方便。(註:那些API的實際使用方法應該不是這樣子,我只是隨便舉個例子。但總之通常只會更複雜)

執行SELECT SQL
希望你不會覺得光是連線就亂七八糟的,因為下面才要開始操作...orz
在這邊我們先考慮把資料從資料庫中取出來就好,寫入的方法之後會再介紹。
如果你還記得最簡單的SQL:SELECT id, name FROM student
這就是取出student資料表中的id和name兩個欄位。請注意資料庫的SELECT指令只會「取出資料」而不會「排序」--除非你要求他要排序:SELECT * FROM student
ORDER BY id ASC
其中"ASC"是表示遞增排序(遞減排序則是DESC)

在這邊要注意幾點事情:

  • SQL指令可以換行,所以我建議你在寫比較長的指令時,擅用換行來讓指令比較好理解
  • SQL的關鍵字不限大小寫,所以你也可以寫成"select * from student"。但是我個人覺得把關鍵字都大寫比較容易懂
  • "select *"的星號是特殊用法,意思是「取得所有欄位」。通常我會建議這麼做,因為你不一定知道待會可能會想用到哪些欄位
  • 通常資料庫名稱、資料表名稱、欄位名稱是不需要用引號框起來的,不過你可以為了區別他們和關鍵字而特別這麼做。但是要注意的是在MySQL中,把這類名稱框起來的引號並不是單引號或雙引號,而是鍵盤上左上角Esc鍵下面的那個「`」(請小心不要打成全型字),像是:SELECT `id`, `name`
    FROM `student`
    WHERE `id` < 10
    ORDER BY `id` DESC
  • 如果有字串的話,一定要用單引號(不能用雙引號)。舉例來說:SELECT * FROM student WHERE name = '高睿甫'
    如果字串之中有單引號的話,就要用跳脫字元「\」,像是如果要取出留言板中的特定留言:SELECT *
    FROM message
    WHERE content = '野村克也說\'要讓一個選手墮落很簡單啊,只要稱讚他就行了!\'他說的真是太忠肯了'
其實還有其他要注意的,不過先到此為止吧,我們還得回來看PHP呢....

PHP執行SQL的指令是mysql_query()(注意:當然要先對資料庫連線,再執行mysql_query()才有意義),有時我們會把SQL直接寫在裡面:$res = mysql_query("SELECT * FROM student");
不過如果是像上面有比較長的SQL,我倒是比較建議先另外丟進變數裡:$sql = "SELECT `id`, `name`
FROM `student`
WHERE `id` < 10
ORDER BY `id` DESC";
$res = mysql_query($sql);
也別忘了PHP的字串裡面是可以直接換行的(題外話:如果你有在寫JavaScript的話,請注意JavaScript並不能直接這樣做喔)

有注意到我都把mysql_query()回傳的東西丟給$res變數嗎?那就是我們接下來要操作的東西囉

取出一列列的資料
好,這一個區塊其實才是我寫本文的主要目的....(不過上面講SQL好像花了太多篇幅)
mysql_query()回傳的$res變數是PHP的某種資料結構,沒辦法直接取得所有資料,不過可以「一筆一筆」的取出來(在這裡我是用mysql_fetch_assoc(),你也可以用mysql_fetch_row(),後面會再介紹他們的不同): $res = mysql_query("SELECT * FROM student");
$row1 = mysql_fetch_assoc($res); /*第一列*/
$row2 = mysql_fetch_assoc($res); /*第二列*/
$row3 = mysql_fetch_assoc($res); /*第三列*/
注意我們其實是在執行同一個function,但是每次卻回傳不同的結果喔!當然你應該不會想這麼呆呆的把整張資料表取完,這種時候就得用上迴圈啦。但是面對不知道SQL回傳的結果有幾筆資料的時候,回圈應該要在什麼時候停止呢?
其實mysql_fetch_assoc()(和mysql_fetch_row())都會在「已經沒有資料」的時候回傳false,所以我們只要把取出來的東西丟給while判斷就好啦: $res = mysql_query('SELECT * FROM student');
while($row = mysql_fetch_assoc($res)) {
/* 這裡就看你想對這一筆資料幹嘛*/
echo $row['id']; /*像這樣就可以印出該學生的學號*/
}

流程大概都清楚了,我們就來試試用HTML的表格來顯示出所有學生的學號和姓名吧: $res = mysql_query('SELECT `id`, `name` FROM `student`');
echo '<table>';
while($student = mysql_fetch_assoc($res)) {
echo '<tr>';
echo '<td>';
echo $student['id'];
echo '</td>';
echo '<td>';
echo $student['name'];
echo '</td>';
echo '</tr>';
}
echo '</table>';
像是這樣,不過輸出的部份我自己比較喜歡另一個方式:<table>
<?php
while($student = mysql_fetch_assoc($res)) {
?>
<tr>
<td><?=$student['id']?></td>
<td><?=$student['name']?></td>
</tr>
<?php
}
?>
<table>
就是這樣囉,或是看你想怎麼顯示都可以。
其他注意事項和小撇步
  • 如果你只想取得一筆資料(比方說想用學號來查名字),那就不需要用while回圈了,比方說:$id = 9646515;
    $res = mysql_query("SELECT * FROM student WHERE id = $id");
    $kong = mysql_fetch_assoc($res);
    $name = $kong['name'];
    echo "學號$id 的學生,他的名字是$name";
  • mysql_fetch_assoc()指令只是我自己慣用,如果資料表是你自己設計的,也可以使用mysql_fetch_row()。差別在於回傳回來的array結構: $res = mysql_query("SELECT id FROM student");

    $row1 = mysql_fetch_assoc($res);
    echo $row1['id'];

    $row2 = mysql_fetch_row($res);
    echo $row2[0];

    $row3 = mysql_fetch_array($res);
    echo $row3['id'];
    echo $row3[0];

    mysql_fetch_assoc()的好處是你不用記得欄位順序,但缺點是你必須記得欄位名稱;mysql_fetch_row()則相反。當然你也可以用mysql_fetch_array(),這樣就兩種方式都可以取得想要的資料。
    小提醒:上面的例子中,請注意$row1、$row2、$row3是會取得「不同筆」的資料喔。
  • 實務面上我們常常需要藉由使用者表單來湊出SQL指令,比方說前面舉過的搜尋留言板:$txt = $_GET['search'];
    $sql = "SELECT * FROM message WHERE content = '$txt'";
    // 還記得字串要加上單引號吧..
    不過以這個例子來說,我們又必須小心使用者輸入的東西本身就有單引號。比方說使用者輸入了"我好傷心喔 T_T'",那麼整個SQL丟給MySQL的時候就會變成SELECT * FROM message WHERE content = '我好傷心喔 T_T''然後就發生錯誤啦(因為MySQL看不懂那些引號是怎麼回事)
    這種時候就必須另外用程式把那個單引號解決掉:$txt = str_replace("'", "\\'", $_GET['search']);
    /* 如果你看不懂那個"\\'",就先照做吧....*/
    $sql = "SELECT * FROM message WHERE content = '$txt'";

    通常我只處理單引號,不過你可能也想把其他東西代換掉,就依樣畫葫蘆囉。
  • 承上,如果是用留言的流水號(數字)來查的話,直覺上程式碼應該是:$id = $_GET['id'];
    $sql = "SELECT * FROM message WHERE id = $id";
    可是,如果使用者在表單中,根本不是打數字的話(比方說使用者什麼都沒打),SQL就會變成SELECT * FROM message WHERE id = 於是就產生錯誤了(因為等號後面不是數字)。
    要解決這個狀況有兩個方法,一個是無論該欄位是不是數字都加上引號:$id = str_replace("'", "\\'", $_GET['id']);
    $sql = "SELECT * FROM message WHERE id = '$id'";

    另一個就是使用sprintf()函數,這也是我比較偏好的方法:$sql = "SELECT * FROM message WHERE id = %d";
    $sql = sprintf($sql, $_GET['id']);
    以上兩個方法都可以應付使用者亂打的狀況
善用sprintf()
承上,我們有時會需要比較長的SQL,像是:SELECT * FROM car
WHERE brand = 'BMW'
AND oilCart > 2000
AND price < 700000
如果我們是使用sprintf()的話,就可以用比較容易懂得簡短程式碼來執行(簡潔易懂的程式碼可以幫助自己或他人後續的除錯和更新):$sql = "
SELECT * FROM car
WHERE brand = '%s'
AND oilCart > %d
AND price < %d
";
$sql = sprintf($sql, $_GET['brand'], $_GET['oilCart'], $_GET['price']);
這只是個比較簡單的例子,但如果是遇到像是需要報表、統計這一類需要跨多的資料表的SQL,使用sprintf()的替代方式可以讓你快速的編輯和除錯。

先到此為止吧,我手痠了
......
... read more

Csound - 31-TET on ratio 5


  • Description

    This project shows one probability for dividing the traditional major 17th in just intonation, which has the frequency ratio of 5, into 31 steps. For each step the ratio would be 5^(1/31). In this project, not all 31 steps are used because I was also trying to experiment with a system of tonality and scale.

    While reading the code, always note the difference between scale and pitch number: the former means the order within the scale, while the latter means the number of steps. Variables that presents pitch number would have postfix "PN" within its name.

  • Term to be used
    • Step: the smallest interval in this temperament, frequency ratio of 5^(1/31)
    • Scale: the mode used in this piece (not all 31 pitches are used here)
    • SD(Scale degree): the order(begin at 0) which the pitch has within the scale
    • PN(Pitch number): number of steps between the pitch and the lowest note
    • PO(pseudo-octave): the interval whose frequency is 5:1
    • Cycle: a complete scale within a PO
  • Problems unsolved
    1. There's no guarantee that adding a PO to a consonant interval makes a consonant interval.
    2. I don't know the way to dynamically decide the length of output file. For now, a long enough p3 for i-statement is used.

怎麼算出哪幾個音是和諧音呢?照理說平均律裡除了需要定義的八度之外是不會有符合純率的和諧音的,但是人耳其實可以接受有些許誤差的和諧音,所以就可以這樣算出來(以JavaScript為例): function ETConsonance(ratio, divider) {
var str = '<table border="1"><tr><td> </td>';
var ratioArr = new Array();
for(var i = 1; i <= divider; i++) {
ratioArr[i] = Math.pow(ratio,i/divider);
str += '<th>' + i + '</th>';
}
str += '</tr>';
for(var j = 1; j < 10; j++) {
str += '<tr><th>' + j + '</th>';
for(i = 1; i <= divider; i++) {
var times = j * ratioArr[i];
var style = (Math.abs(times-Math.round(times))<0.05) ? ' style="font-weight: bold;"' : '';
str += '<td' + style + '>' + times.toString().substr(0, 5) + '</td>';
}
str += '</tr>';
}

document.writeln(str);
}
ETConsonance(5, 31);
// 換成2和12就可以得到平常用的12-EDO,你可能會很驚訝3和8跟純律的差距竟是這麼大
結果就會是:

如上,粗體字就是可以考慮的"接近"和諧的音程。如第三音的六倍頻接近基準音的七倍,即3個最小音程的頻率比接近7:6;依此類推8個最小音程的頻率比接近3:2
至於建構在主音上要怎麼配置音階,我參考了12平均律中大調音階的一些現象:
  1. 3和4(半因數)都是和諧音程,但是與主音相差三個和四個半音的兩個音並不會同時出現
  2. do到fa的關係同於sol到高音do的關係(馬老師提到的某種對稱性,我忘記專有名詞了..QQ)

由第一點可知會造成"風格"的音階通常不會把所有跟主音成和諧關係的所有音級都用進去;至於第二點我則決定不在此次使用(但是我另外有考慮到不同於大調音階,而是改用反向對稱的方式)。

但是目前為止仍然有一些其他的問題。首先由於一個音階的循環(又稱假八度pseudo-octave,於本例中頻率比是5,以下簡稱PO)並不是2的倍數,所以把某個音移高一個PO所得到的音並不一定跟原本的狀態類似。舉例來說某兩個音的頻率成7:6的關係,若將高音者一高一個PO之後會得到35:6,原則上雖然仍接近整數比,但是和諧度卻是變低了。「和諧度變低」的另一個佐證是把傳統西樂的兩個完全五度相疊會得到不太和諧的大二度,而若由此推論,可知移高傳統的八度也會造成和諧度變低,只是因為2是最小的質數,所以和諧度的差異沒有那麼大。(事實上,隨著音程的增大,不和諧的感覺也會由於低音的泛音不足以與高音者產生交互作用而減少)
其他發現:12-EDO中,若X和Y成和諧關係(半音數3, 4, 5, 7, 8, 9),則將其中一音移高和移低大三度的結果勢必至少有一仍是和諧。

後記:
馬老師說我這學期可以用這個抵其他的作業。花了整整兩天證明自己仍然是個菜鳥,很多想要的功能都弄不出來。很難得的這次寫程式雖然不太順,但寫得還蠻平心靜氣的(也許是因為剛好穿著資工系服的關係^^|||b)。我想當初跟學長姐們一起學Csound的時候,其實我並沒有比較厲害或是花了比較少的時間,只是因為比較習慣寫程式而且有自信可以做完想要做的功能,所以壓力比較小吧。看樣子下學期還是要跟著上Csound才好(當然是期中之後再說XD)

iJigg我一直連不上,shopping前幾天也說她連不到Blog上面的內嵌播放器..總之我後來還是用經由Hans教的方法:把Box.net當免空用

話說這篇花了我兩個小時(HTML碼就接近11KB...orz),有點想乾脆拿來當論文題材XD..不過音律的問題大概早在幾百年前就被研究到爛掉了,就算是電腦應用上也..也許來個任意律制的自動作曲?

然後我還邀請了馬老師來看這篇..希望推論過程不會被批說太慘(其實看了幾遍之後就覺得寫這樣的東西還蠻小兒科的)......
... read more

Max 自動作曲

http://www.box.net/rssdownload/122326921/MaxAC20080108.zip

陽春到甚至不配稱作"自動作曲"..全班六個人裡最簡陋的..
只有大調的六個順階和弦,不允許非調性內音
旋律未解決,亦不平順

不過怎地到了昨天才想到這種模組的方法

分三部份
1. Chord Progression: 用前一個和弦來決定下個調性和和弦
2. Accompaniment
3. Melody (w/o resolution)

如果後續要做的話都可以分開改:P

......
... read more

Re: [音樂] 自動作曲

作者: kong (Life of Music) 看板: P_LifeOMusic
標題: Re: [音樂] 自動作曲
時間: Fri Dec 14 03:40:34 2007

※ 引述《kong (Life of Music)》之銘言:
> http://kong.dorm7.nctu.edu.tw/ac/

(請找PHP檔)
> 還好目前還很難聽
http://kong.dorm7.nctu.edu.tw/ac/20071214.php

雖然規則還是很死板

不過加了點伴奏之後增加了不少可聽性

...至少可以平心靜氣地把100個小節聽完


本學期(就課業要求上)應該就到此為止吧

--
就算是這個世界 把我拋棄
而至少快樂傷心我自己決定
http://www.streetvoice.com.tw/kong0107/music

......
... read more

Csound - Final Project

MP3下載

這次的比較能聽了....
/*
Csound - Final Project
Author: Kong Kao
Advisor: Prof. James Ma

This project uses pre-entered chords to implement auto-composition.
Chords are stored by their roots in tables.
Instr 1 decides how the chord goes, and store the way the music would go
into a function table (giftMelody).
Instr 2 does 3 part of music:
1. The root of the chord.
2. A melody generated by an algorithm with random.
3. Accompaniment
*/
<CsoundSynthesizer>
<CsOptions>
</CsOptions>

<CsInstruments>
; Initialize the global variables.
sr = 44100
kr = 4410
ksmps = 10
nchnls = 1

; Global a-rate variabe used for reverberation
gaSig init 0

; Some constant for convenience
giSemiTone init 2^(1/12)
giLoopLength init 8 ; Seconds a period would use.

; Function tables
giftScale init 2
giftMelody ftgen 0, 0, 8192, -2, 0, 8192, 0 ; Overall roots of chords

; Function tables that stores the chords.
; The name of them is only meaningful for me, so don't mind it...XD
giftBallad init 1
giftHappy init 4
giftToDearYou init 5


instr 1
/*
No signal output, but need to have the same length with i2.
Used to assign the overall roots to the function table giftMelody
Note that only the value is neither semitones nor intervals, but the
index of the tone in giftScale.
*/
aPhasor phasor 1/giLoopLength
INIT0:
iSwitch = floor(rnd(3))
if( iSwitch == 0 ) then
iFt = giftBallad
elseif( iSwitch == 1 ) then
iFt = giftHappy
else
iFt = giftToDearYou
endif
aMelody table aPhasor, iFt, 1
timout 0, giLoopLength, CONT0
reinit INIT0
CONT0:
rireturn
aPhasor phasor 1/p3
tablew aMelody, aPhasor, giftMelody, 1
endin


instr 2
iUnit = 0.25 ; The shortest length of a note, measured in seconds
iAmp = ampdb( p4 )
iBaseFreq = p5

kPhasor phasor 1/p3
kMelody table kPhasor, giftMelody, 1
kPitch table kMelody, giftScale
kFreq = iBaseFreq * giSemiTone ^ kPitch / 2
aSig1 oscil iAmp, kFreq, 11

; ----------------------------------------------------------
iPrevPitch = 3
iPrevInterval = 0
iPosition = 0
/*
Use a variable to store the position of the current note in the period.
This is used to decide which chord it is for the note.
*/
INIT2:
/*
Decide the interval of this note with the root of the chord.
If the previous interval tends to be balanced (such as 4th tends to
3rd, and 7th tends to octave), set the interval to the tension. (Note
that this may go wrong as the chord changes)
Note that the "interval" is stored one less than what we call it.
*/
if( iPrevInterval == 3 ) then
iInterval = 2
elseif( iPrevInterval == 6 ) then
iInterval = 7
else
INTERV2:
iInterval table rnd(ftlen(3)), 3
cigoto ( iInterval < 0 ), INTERV2
endif

/*
Fetch what the chord here it is and calculate the tone to be played.
If the interval with the previous note is bigger than octave,
then choose the interval again randomly.
*/
iRootIndex table iPosition/p3, giftMelody, 1, 0, 1
iPitch table iRootIndex + iInterval, giftScale
cigoto ( abs( iPitch - iPrevPitch ) > 12 ), INTERV2

/*
Decide the length of this note.
For disonant tones (those not in the triad chord of the given root),
set the length of it short.
*/
if( iInterval != 0 \
&& iInterval != 2 \
&& iInterval != 4 \
&& iInterval != 7 \
) then
iDur = iUnit
else
iDur = (int(rnd(3))+1)*iUnit
endif

kAmp expseg 0.01, 0.1, 1, iDur-0.2, 0.25, 0.1, 0.01

iFreq = iBaseFreq * giSemiTone ^ iPitch
kVibEnv linseg 0, 0.3, 0, iDur-0.3, 0.5
kVibAmp lfo kVibEnv, 10
kFreq = iFreq * ( 1 + kVibAmp )

kForm line 600, iDur, 800
kBand line 60, iDur, 80
aSig2 fof iAmp * kAmp, kFreq, kForm, 0, kBand, \
0.005, 0.02, 0.07, 64, 14, 15, iDur


if( iDur > 0.3 ) then
gaSig = gaSig + aSig2 / 2
endif
iPosition = iPosition + iDur
iPrevPitch = iPitch
iPrevInterval = iInterval
timout 0, iDur, CONT2
reinit INIT2
CONT2:
rireturn

; ---------------------------------------------------------------------
iPosition3 = 0
iPrevPitch31 = 0
iPrevPitch32 = 0
INIT3:
/*
Decide 2 tones to play.
floor(rnd(3))*2 is used to decide the semitones with the root would be,
that is, they're deciding whether the the interval is 1st, 3rd, or 5th.
*/
iRootIndex3 table iPosition3/p3, giftMelody, 1, 0, 1
iPitch31 table iRootIndex3 + floor(rnd(3))*2, giftScale
iPitch32 table iRootIndex3 + floor(rnd(3))*2, giftScale
iFreq31 = iBaseFreq * giSemiTone ^ iPitch31 * 2 ^ floor(birnd(1))
iFreq32 = iBaseFreq * giSemiTone ^ iPitch32 * 2 ^ floor(rnd(2))

kAmp3 linseg 0, 0.2, 1, iUnit-0.2, 0
aSig31 oscil iAmp*kAmp3, iFreq31/2, 13
aSig32 oscil iAmp*kAmp3, iFreq32/2, 12

if( iFreq31 < 220 ) then
gaSig = gaSig + aSig31 * abs( 220 - iFreq31 ) / 220
endif

iPrevPitch31 = iPitch31
iPrevPitch32 = iPitch32
iPosition3 = iPosition3 + iUnit
timout 0, iUnit, CONT3
reinit INIT3
CONT3:
rireturn
aSig3 = ( aSig31 + aSig32 ) / 2


aSig sum aSig1/2, aSig2, aSig3
kAmp linseg 1, p3-4, 1, 4, 0.01
aSig = aSig * kAmp
out aSig
endin

instr 101
aSig reverb gaSig, 0.5
out aSig
gaSig = 0
endin


</CsInstruments>

<CsScore>
/*
Function table 1 and 4:
Stores the roots of the chords by the index of the note in f2.
By using GEN7 and big enough table, we can then set the chord to change
in a nonregular way, that is, chord may change not only at the 1st beat
of a bar.
*/
f 1 0 64 -7\
3 8 3 0 \
7 8 7 0 \
8 8 8 0 \
7 8 7 0 \
6 8 6 0 \
5 8 5 0 \
6 8 6 0 \
7 4 7 0 \
0 4 0

f 4 0 16 -7\
3 4 3 0 \
1 4 1 0 \
6 4 6 0 \
7 2 7 0 \
0 2 0

f 5 0 32 -7\
6 4 6 0 \
7 4 7 0 \
3 2 3 0 \
2 2 2 0 \
1 4 1 0 \
6 4 6 0 \
7 4 7 0 \
3 8 3

/*
Function table 2
Stores major scale in numbers of semitones
between the tonic and each one.
*/
f 2 0 16 -2 \
-5 -3 -1 0 2 4 5 7 \
9 11 12 14 16 17 19 21

/*
Function table 3
Stores the probability of an interval to be used.
More a number appears, more the inteval would be used.
*/
f 3 0 64 -7 \
0 5 0 0 \
1 5 1 0 \
2 10 2 0 \
3 3 3 0 \
4 7 4 0 \
5 12 5 0 \
6 2 6 0 \
7 8 7 0 \
-1 64 -1

/*
Function tables for timbre
*/
f 11 0 8193 10 1 0.5 0.25
f 12 0 8193 10 1 0.1 0.2 0.3 0.4 0.3 0.2 0.1
f 13 0 8193 10 1 0.4 0.3 0.2 0.1
f 14 0 8193 10 1
f 15 0 8192 19 0.5 0.5 260 0.5


i 1 0 16
i 2 0 16 75 220
i 101 0 17
e

</CsScore>
</CsoundSynthesizer>
......
... read more

Csound作業五 - 自動作曲??


/*
Csound Exercise 05
Author: Kong Kao
Advisor: Prof. James Ma

This exercise focuses on conditional branch, including opcodes such as
reinit, rireturn, and timout....
The basic idea of this piece is an algorithm to compose in a single mode.
The function table stores different data and may be used in different way.

For future work, I'd like to add other possibility parameters to make the
piece has a feeling of tonic and some rhythm.
*/
<CsoundSynthesizer>
<CsOptions>
</CsOptions>

<CsInstruments>
; Initialize the global variables.
sr = 44100
kr = 4410
ksmps = 10
nchnls = 2


; Some constant for convenience
giScale init 1
giInterval init 2
giSemiTone init 2^(1/12)

gaSig1 init 0

instr 1
iBaseFreq = p4
iAmp = p5
iAmp2 = p6
iAmp3 = p7
iPrevPitchIndex init 9

INIT:
;seed rnd(2^32)
; Randomly choose an interval to decide the next note
iTemp = rnd( ftlen(giInterval) )
iInterval table iTemp, giInterval
cigoto ( iInterval < 0 ), INIT

; Decide the melody would go up or down
if( birnd(1) > 0 ) then
iPitchIndex = iPrevPitchIndex + iInterval
else
iPitchIndex = iPrevPitchIndex - iInterval
endif
cigoto ( iPitchIndex < 0 ), INIT
cigoto ( iPitchIndex >= ftlen(giScale) ), INIT
iPitch table iPitchIndex, giScale

; Decide other tones
; 3rd, 4th, 5th, 6th would be consonant. Care about neither A4 nor d5.
; The 3 tones would be a triad.
iVerticalInterval = floor( rnd(4) ) + 3
if( iVerticalInterval == 3 ) then
iVerticalInterval2 = 5 + floor( rnd(2) )
elseif( iVerticalInterval == 4 ) then
iVerticalInterval2 = 6
elseif( iVerticalInterval == 5 ) then
iVerticalInterval2 = 3
elseif( iVerticalInterval == 6 ) then
iVerticalInterval2 = 3 + floor( rnd(2) )
endif

if( iPitchIndex < ftlen( giScale ) / 2 ) then
iPitch2 table iPitchIndex + iVerticalInterval, giScale
iPitch3 table iPitchIndex + iVerticalInterval2, giScale
else
iPitch2 table iPitchIndex - iVerticalInterval, giScale
iPitch3 table iPitchIndex - iVerticalInterval2, giScale
endif

iFreq = iBaseFreq * giSemiTone^iPitch
iFreq2 = iBaseFreq * giSemiTone^iPitch2 / 4 ; Bass melody
iFreq3 = iBaseFreq * giSemiTone^iPitch3

; Choose a duration
iDur = (int(rnd(4))+1)*0.25

; Amplitude
kPhase phasor 1/iDur
kTableIndex = kPhase * ftlen(21)
kAmp table3 kTableIndex, 21 + floor( rnd(4) )
kAmp2 table3 kTableIndex, 21 + floor( rnd(4) )
kAmp3 table3 kTableIndex, 21 + floor( rnd(4) )
kAmp = kAmp * iAmp
kAmp2 = kAmp2 * iAmp2
kAmp3 = kAmp3 * iAmp3

/*
Since I'd like to assign the timbre of the bass dynamically
The signal processing would be here, instead of after label CONT
*/
aSig2S oscili kAmp2, iFreq2, 12
aSig2E oscili kAmp2, iFreq2, 14 + floor(rnd(3))
kLine line 0, iDur, 1
aSig2 = aSig2S * kLine + aSig2E * ( 1 - kLine )

; Store the pitch index for next note to use.
iPrevPitchIndex = iPitchIndex
timout 0, iDur, CONT
reinit INIT
CONT:
aSig1 oscili kAmp, iFreq, 11
aSig3 oscili kAmp3, iFreq3, 13
aSig = aSig1 + aSig2 + aSig3
rireturn
kAmpOverAll linseg 0, p3/6, 0.7, p3*2/3, 0.9, p3/6, 0
aSig = aSig * kAmpOverAll

outs aSig, aSig
endin

</CsInstruments>

<CsScore>
/*
Function table 1
Stores a scale in numbers of semitones between the tonic and each one.
It must be a diatonic scale in this exercise to let the contrapound
consonant tone to go right..(in natural 3rd, 4th, 5th, 6th,
instead of any other dissonant intervals.)
For now, this table stores a harmonic minor.
*/
f 1 0 16 -2 \
-4 -1 0 2 3 5 7 8 11 12 14 15 17 19 21 22

/*
Function table 2
Stores the possibility of each interval, which would be used randomly.
For example, if the number denoting an interval has the amount of
appearance to be 6, then the possibility of the interval to be used
would be "6 / ftlen(thisTable)"
For simplicity, 0 would mean the interval to be perfect 1st, but -1 is
also used for the random procedure to goes again.
*/
f 2 0 64 -7 \
0 4 0 0 \
1 8 1 0 \
2 7 2 0 \
3 6 3 0 \
4 5 4 0 \
5 4 5 0 \
6 3 6 0 \
7 2 7 0 \
8 1 8 0 \
-1 64 -1 \

; Function tables for timbre....
f 11 0 8193 10 \
8 4 2 1
f 12 0 8193 10 \
1 2 3 4
f 13 0 8193 10 \
4 3 2 1
f 14 0 8193 11 10 1 0.5
f 15 0 8193 11 8 1 1.5
f 16 0 8193 11 12 1 -0.5

; Envelopes
f 21 0 8193 5 \
0.1 1024 1 1024 0.7 4096 0.4 2048 0.01
f 22 0 8193 8 \
0 1024 1 1024 0.7 4096 0.4 2048 0
f 23 0 8193 5 \
0.1 1024 1 6144 0.7 1024 0.01
f 24 0 8193 8 \
0.1 1024 1 6144 0.7 1024 0.01

i 1 0 60 220 12000 16000 8000
e

</CsScore>
</CsoundSynthesizer>
......
... read more