まずは、こちらを参考にさせて頂いて、MPDとMPCをインストールしました。前述サイトの丸写しですが、一応・・・。
sudo apt-get install mpd mpc
ここで、mpd は先程の Music Player Daemon で、mpc がそれに対するクライアントソフトです。
次に alsa-util をインストールしますが、自分の環境では最新版が入っていて、特に更新もされませんでした。
sudo apt-get install alsa-utils
一旦、再起動。
sudo reboot
mpd の設定ファイルを編集します。
sudo vi /etc/mpd.conf
以下をコメントアウト。
→ #bind_to_address localhost
audio_output { ~ の記述部分のformat,mixer_xxxをコメントアウト。
audio_output {
type "alsa"
name "My ALSA Device"
device "hw:0,0" # optional
→ # format "44100:16:2" # optional
→ # mixer_device "default" # optional
→ # mixer_control "PCM" # optional
→ # mixer_index "0" # optional
}
サービスを再起動。
sudo /etc/init.d/mpd restart
次に音楽ファイルを用意して、 /var/lib/mpd/music フォルダ下に格納します。他のところに置きたい場合はシンボリックリンクでも大丈夫なようです。音楽ファイルを用意して聞く方法は、こちら等を参考にして試しましたが、今回はインターネットラジオを構成したいと思い調べてみました。結果としては playlist にネットラジオのURL(放送チャネルが通常ポート番号で振り分けられているので、URL:ポートNo まで)を記述すれば、通常の音楽ファイルと同様に聞くことができることがわかりました。
例えば、プレイリストを/var/lib/mpd/playlists フォルダに radio.m3u のファイル名でテキストエディタで作成して、
70s-90s J-pop Stream
http://198.178.123.14:7002
Retro 70s 80s 90s
http://192.81.248.91:8300
Golden 50/70s Hits
http://listen.radionomy.com/golden-50-70s-hits
Hits80
http://listen.radionomy.com/Hits80
STAR FM Rock Classics
http://91.250.82.237:8008
ABC Jazz
http://listen.radionomy.com/ABC-Jazz
kreyolmixradio
http://174.36.232.227:8132
8bitx Radio Network
http://198.178.123.2:7492
のように記述しておいて、
mpc clear
mpc load radio.m3u
mpc play
として、プレイリストを再生するとリストのトップのネットラジオチャネルが再生されます。あとは、
mpc next
mpc prev
なんかで、リスト内のチャネルを切り替えることができます。自分は、/var/lib/mpd/下のmusicとplaylistsフォルダは削除してしまい、/home/pi/ 下に同名のフォルダを作成して、シンボリックリンクでそれらを見に行くようにしました。そのほうが、piユーザで音楽ファイルのコピーやプレイリストの編集ができるので。
ちなみに、Raspberry Piで音声を再生するには、アナログ端子へ出力、HDMIへ出力、USBにDACをつないで出力等のいくつかの方法がありますが、今回は一番シンプルなアナログ端子を使いました。とりあえずは、ヘッドフォンをアナログ端子につないで動作を確認しました。
とりあえず、ソフトウェア的にはインターネットラジオを構成できたわけですが、これだけだと、おもしろくありません。がらくた部屋としては、どちらかというと、ここからが本題・・・。
せっかくのB+でGPIOがいっぱいあるので、チャネルの選択と音量の調節をGPIOに接続したロータリエンコーダで行えるようにしてみたいと思います。ただし、ロータリーエンコーダに関しても、すでに先人がいらっしゃいますので、それを使わせてもらうことにします。
GPIOから端子を引き出すのと一部LED用の抵抗を入れるための回路をハンダ付け。そういえば、ハンダ付けするの久しぶり。ハンダ付けって楽しいですよね。♫それっハンダ!それっハンダ~♫
一応、こんな感じで、
ソフトの方は、こんな感じ。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
wiring BCM BCM wiringPi | |
function Pi GPIO Name pin pin Name GPIO Pi function | |
------------------------------------------------------------- | |
Vcc <-+3V3 +3V3 1 2 +5V + 5V | |
SDA <- 8 GPIO2 SDA1 3 4 +5V + 5V | |
SCL <- 9 GPIO3 SCL1 5 6 GND GND | |
7 GPIO4 GCLK 7 8 TXD0 GPIO14 15 | |
GND -> GND GND 9 10 RXD0 GPIO15 16 | |
Enc1_A -> 0 GPIO17 GEN0 11 12 GEN1 GPIO18 1 <- Enc1_B | |
LED1_R <- 2 GPIO27 GEN2 13 14 GND GND | |
LED1_G <- 3 GPIO22 GEN3 15 16 GEN4 GPIO23 4 -> LED1_B | |
+3V3 +3V3 17 18 GEN5 GPIO24 5 -> LCD_BKL | |
12 GPIO10 MOSI 19 20 GND GND | |
13 GPIO9 MISO 21 22 GEN6 GPIO25 6 | |
14 GPIO11 SCLK 23 24 CE0_N GPIO8 10 | |
GND GND 25 26 CE1_N GPIO7 11 | |
30 EEPROM ID_SD 27 28 ID_SC EEPROM 31 | |
Enc2_A -> 21 GPIO5 29 30 GND GND | |
ENC2_B -> 22 GPIO6 31 32 GPIO12 26 | |
LED2_R <- 23 GPIO13 33 34 GND GND | |
LED2_G <- 24 GPIO19 35 36 GPIO16 27 | |
LED2_B <- 25 GPIO26 37 38 GPIO20 28 | |
GND GND 39 40 GPIO21 29 | |
------------------------------------------------------------ | |
LEDx_x端子は220Ωを入れてRPiの各端子へ。RPi端子LOWで点灯。 | |
EncorderからのA,B相信号入力はRPi側でエッジ変化割り込みで関数呼び出しへ。 | |
チャネル用エンコーダが Enc1_A,Bで内蔵RGB LEDがLED1_R,G,B。 | |
ボリューム用エンコーダが Enc2_A,Bで内蔵RGB LEDがLED2_R,G,B。 | |
I2CにはストリナのI2C LCDを接続。 | |
*/ | |
#include <iostream> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "wiringPi.h" | |
#include "I2C_LDC.h" | |
FILE *fp; | |
char str[256]; | |
int tune_A, tune_B, pre_tune_A, pre_tune_B, tune_num; | |
int vol_A, vol_B, pre_vol_A, pre_vol_B, volume; | |
I2C_LCD lcd; | |
// チューニング(チャネル)用エンコーダエッジのA相エッジ変化検出割り込みでコールされる関数。 | |
void click_tune_A(void) | |
{ | |
int _a; | |
// WiringPi GPIO_0(11pin)値取得。 | |
_a = digitalRead(0); | |
// 値が変わった場合、 | |
if (_a != tune_A) | |
{ | |
// 一つ前のA端子の値を格納。 | |
if (tune_A == 1) | |
{ | |
pre_tune_A = 1; | |
} | |
else | |
{ | |
pre_tune_A = 0; | |
} | |
tune_A = _a; | |
// A端子とB端子の直前の値が1で、今の値が0なら | |
if (tune_A == 0 && tune_B == 0 && pre_tune_A == 1 && pre_tune_B == 1) | |
{ | |
printf("left %d\n", --tune_num); | |
system("mpc prev > radio_status.txt"); | |
system("mpc play > radio_status.txt"); | |
system("mpc status > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 16, fp); | |
fclose(fp); | |
printf("%s\n", str); | |
lcd.setCursor(0, 0); | |
lcd.puts(" "); | |
lcd.setCursor(0, 0); | |
lcd.puts(str); | |
switch (abs(tune_num % 3)) | |
{ | |
case 0: | |
digitalWrite(2, LOW); | |
digitalWrite(3, HIGH); | |
digitalWrite(4, HIGH); | |
break; | |
case 1: | |
digitalWrite(2, HIGH); | |
digitalWrite(3, LOW); | |
digitalWrite(4, HIGH); | |
break; | |
case 2: | |
digitalWrite(2, HIGH); | |
digitalWrite(3, HIGH); | |
digitalWrite(4, LOW); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
} | |
// チューニング(チャネル)用エンコーダエッジのB相エッジ変化検出割り込みでコールされる関数。 | |
void click_tune_B(void) | |
{ | |
int _b; | |
// WiringPi GPIO_1(12pin)値取得。 | |
_b = digitalRead(1); | |
if (_b != tune_B) | |
{ | |
if (tune_B == 1) | |
{ | |
pre_tune_B = 1; | |
} | |
else | |
{ | |
pre_tune_B = 0; | |
} | |
tune_B = _b; | |
if (tune_A == 0 && tune_B == 0 && pre_tune_A == 1 && pre_tune_B == 1) | |
{ | |
printf("right %d\n", ++tune_num); | |
system("mpc next > radio_status.txt"); | |
system("mpc play > radio_status.txt"); | |
system("mpc status > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 16, fp); | |
fclose(fp); | |
printf("%s\n", str); | |
lcd.setCursor(0, 0); | |
lcd.puts(" "); | |
lcd.setCursor(0, 0); | |
lcd.puts(str); | |
switch (abs(tune_num % 3)) | |
{ | |
case 0: | |
digitalWrite(2, LOW); | |
digitalWrite(3, HIGH); | |
digitalWrite(4, HIGH); | |
break; | |
case 1: | |
digitalWrite(2, HIGH); | |
digitalWrite(3, LOW); | |
digitalWrite(4, HIGH); | |
break; | |
case 2: | |
digitalWrite(2, HIGH); | |
digitalWrite(3, HIGH); | |
digitalWrite(4, LOW); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
} | |
// ボリューム用エンコーダエッジのA相エッジ変化検出割り込みでコールされる関数。 | |
void click_vol_A(void) | |
{ | |
int _a; | |
// WiringPi GPIO_21(29pin)値取得。 | |
_a = digitalRead(21); | |
// 値が変わった場合、 | |
if (_a != vol_A) | |
{ | |
// 一つ前のA端子の値を格納。 | |
if (vol_A == 1) | |
{ | |
pre_vol_A = 1; | |
} | |
else | |
{ | |
pre_vol_A = 0; | |
} | |
vol_A = _a; | |
// A端子とB端子の直前の値が1で、今の値が0なら | |
if (vol_A == 0 && vol_B == 0 && pre_vol_A == 1 && pre_vol_B == 1) | |
{ | |
volume = volume - 1; | |
if (volume > 100) volume = 100; | |
sprintf(str, "amixer set PCM %d%% > radio_status.txt", volume); | |
system(str); | |
system("amixer | egrep '\[.*%\]' > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 30, fp); | |
sprintf(str, strchr(str, '[')); | |
fclose(fp); | |
printf("Volume:%s\n", str); | |
lcd.setCursor(4, 1); | |
lcd.puts(" "); | |
lcd.setCursor(4, 1); | |
lcd.puts(str); | |
switch (abs(volume % 3)) | |
{ | |
case 0: | |
digitalWrite(23, LOW); | |
digitalWrite(24, HIGH); | |
digitalWrite(25, HIGH); | |
break; | |
case 1: | |
digitalWrite(23, HIGH); | |
digitalWrite(24, LOW); | |
digitalWrite(25, HIGH); | |
break; | |
case 2: | |
digitalWrite(23, HIGH); | |
digitalWrite(24, HIGH); | |
digitalWrite(25, LOW); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
} | |
// ボリューム用エンコーダエッジのB相エッジ変化検出割り込みでコールされる関数。 | |
void click_vol_B(void) | |
{ | |
int _b; | |
// WiringPi GPIO_22(31pin)値取得。 | |
_b = digitalRead(22); | |
if (_b != vol_B) | |
{ | |
if (vol_B == 1) | |
{ | |
pre_vol_B = 1; | |
} | |
else | |
{ | |
pre_vol_B = 0; | |
} | |
vol_B = _b; | |
if (vol_A == 0 && vol_B == 0 && pre_vol_A == 1 && pre_vol_B == 1) | |
{ | |
volume = volume + 1; | |
if (volume < 0) volume = 0; | |
sprintf(str, "amixer set PCM %d%% > radio_status.txt", volume); | |
system(str); | |
system("amixer | egrep '\[.*%\]' > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 30, fp); | |
sprintf(str, strchr(str, '[')); | |
fclose(fp); | |
printf("Volume:%s\n", str); | |
lcd.setCursor(4, 1); | |
lcd.puts(" "); | |
lcd.setCursor(4, 1); | |
lcd.puts(str); | |
switch (abs(volume % 3)) | |
{ | |
case 0: | |
digitalWrite(23, LOW); | |
digitalWrite(24, HIGH); | |
digitalWrite(25, HIGH); | |
break; | |
case 1: | |
digitalWrite(23, HIGH); | |
digitalWrite(24, LOW); | |
digitalWrite(25, HIGH); | |
break; | |
case 2: | |
digitalWrite(23, HIGH); | |
digitalWrite(24, HIGH); | |
digitalWrite(25, LOW); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
} | |
int main(int argc, char *argv[]) | |
{ | |
int setup = 0; | |
int fd; | |
char *i2cFileName = "/dev/i2c-1"; | |
// 変数初期化。 | |
tune_A = 1; | |
tune_B = 1; | |
pre_tune_A = 0; | |
pre_tune_B = 0; | |
tune_num = 0; | |
vol_A = 1; | |
vol_B = 1; | |
pre_vol_A = 0; | |
pre_vol_B = 0; | |
volume = 80; | |
// wiringPの初期化。 | |
setup = wiringPiSetup(); | |
// GPIO_0(11pin)をプルアップして、エッジ検出割り込み設定。 | |
pullUpDnControl(0, PUD_UP); | |
wiringPiISR(0, INT_EDGE_BOTH, click_tune_A); | |
// GPIO_1(12pin)をプルアップして、エッジ検出割り込み設定。 | |
pullUpDnControl(1, PUD_UP); | |
wiringPiISR(1, INT_EDGE_BOTH, click_tune_B); | |
// GPIO_21(29pin)をプルアップして、エッジ検出割り込み設定。 | |
pullUpDnControl(21, PUD_UP); | |
wiringPiISR(21, INT_EDGE_BOTH, click_vol_A); | |
// GPIO_22(31pin)をプルアップして、エッジ検出割り込み設定。 | |
pullUpDnControl(22, PUD_UP); | |
wiringPiISR(22, INT_EDGE_BOTH, click_vol_B); | |
// GPIO_2,3,4,5,23,24,25(13,15,16,18,33,35,37pin)をプルアップダウンオフにして、出力に設定。 | |
pullUpDnControl(2, PUD_OFF); | |
pinMode(2, OUTPUT); | |
pullUpDnControl(3, PUD_OFF); | |
pinMode(3, OUTPUT); | |
pullUpDnControl(4, PUD_OFF); | |
pinMode(4, OUTPUT); | |
pullUpDnControl(5, PUD_OFF); | |
pinMode(5, OUTPUT); | |
pullUpDnControl(23, PUD_OFF); | |
pinMode(23, OUTPUT); | |
pullUpDnControl(24, PUD_OFF); | |
pinMode(24, OUTPUT); | |
pullUpDnControl(25, PUD_OFF); | |
pinMode(25, OUTPUT); | |
// エンコーダライト出力の初期化。 | |
digitalWrite(2, LOW); | |
digitalWrite(3, HIGH); | |
digitalWrite(4, HIGH); | |
digitalWrite(23, LOW); | |
digitalWrite(24, HIGH); | |
digitalWrite(25, HIGH); | |
// LCDバックライトOn。 | |
digitalWrite(5, HIGH); | |
// I2CポートをRead/Write属性でオープン。 | |
if ((fd = open(i2cFileName, O_RDWR)) < 0) | |
{ | |
printf("Faild to open i2c port\n"); | |
exit(1); | |
} | |
// LCD初期化。 | |
lcd.init(fd); | |
lcd.clear(); | |
lcd.setCursor(0, 1); | |
lcd.puts("Vol:"); | |
// mpc 再生。 | |
system("mpc clear > radio_status.txt"); | |
system("mpc load radio.m3u"); | |
system("mpc play > radio_status.txt"); | |
system("mpc status > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 16, fp); | |
fclose(fp); | |
printf("%s\n", str); | |
lcd.setCursor(0, 0); | |
lcd.puts(str); | |
system("amixer | egrep '\[.*%\]' > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 30, fp); | |
sprintf(str, strchr(str, '[')); | |
fclose(fp); | |
printf("Volume:%s\n", str); | |
lcd.setCursor(0, 1); | |
lcd.puts("Vol:"); | |
lcd.puts(str); | |
while (1) | |
{ | |
switch (getchar()) | |
{ | |
case 'n': | |
system("mpc next > radio_status.txt"); | |
system("mpc play > radio_status.txt"); | |
system("mpc status > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 16, fp); | |
fclose(fp); | |
printf("%s\n", str); | |
lcd.setCursor(0, 0); | |
lcd.puts(" "); | |
lcd.setCursor(0, 0); | |
lcd.puts(str); | |
break; | |
case 'p': | |
system("mpc prev > radio_status.txt"); | |
system("mpc play > radio_status.txt"); | |
system("mpc status > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 16, fp); | |
fclose(fp); | |
printf("%s\n", str); | |
lcd.setCursor(0, 0); | |
lcd.puts(" "); | |
lcd.setCursor(0, 0); | |
lcd.puts(str); | |
break; | |
case 's': | |
system("mpc play > radio_status.txt"); | |
system("mpc status > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 16, fp); | |
fclose(fp); | |
printf("%s\n", str); | |
lcd.setCursor(0, 0); | |
lcd.puts(str); | |
break; | |
case 'u': | |
volume = volume + 5; | |
if (volume > 100) volume = 100; | |
sprintf(str, "amixer set PCM %d%% > radio_status.txt", volume); | |
system(str); | |
system("amixer | egrep '\[.*%\]' > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 30, fp); | |
sprintf(str, strchr(str, '[')); | |
fclose(fp); | |
printf("Volume:%s\n", str); | |
lcd.setCursor(4, 1); | |
lcd.puts(" "); | |
lcd.setCursor(4, 1); | |
lcd.puts(str); | |
break; | |
case 'd': | |
volume = volume - 5; | |
if (volume < 0) volume = 0; | |
sprintf(str, "amixer set PCM %d%% > radio_status.txt", volume); | |
system(str); | |
system("amixer | egrep '\[.*%\]' > radio_status.txt"); | |
fp = fopen("radio_status.txt", "r"); | |
fgets(str, 30, fp); | |
sprintf(str, strchr(str, '[')); | |
fclose(fp); | |
printf("Volume:%s\n", str); | |
lcd.setCursor(4, 1); | |
lcd.puts(" "); | |
lcd.setCursor(4, 1); | |
lcd.puts(str); | |
break; | |
case 'e': | |
system("mpc stop > radio_status.txt"); | |
return 0; | |
break; | |
default: | |
break; | |
} | |
delay(1); | |
} | |
return 0; | |
} |
動かしてみたところ。
エンコーダでプレイリストの切り替えとボリュームの変更を行えるようにしました。これをケースに入れようと思ったんですが、手頃な入れ物が見つからなかったで、
今回は、手っ取り早く作りたかったので、USB DACも使わずにアナログ出力で、アンプ、スピーカもモノラルのチープなものを使ったんですが、逆にAMラジオな雰囲気のノイジーな音で、50's~70'sのオールディーズチャネルなんか聞くと、すごくいい感じ。
0 件のコメント:
コメントを投稿