なぜハイレゾは「バカげている」のかを読んで。
記事の内容についてはさておいて、リンクされてるWAVファイル再生してみたところ、歪みなどは全く何も聞こえませんでした。ONKYO SE-U33GXV2はお値段の割にとても良いアンプだと思います。
以前22kHz Sin波を再生させて、オシロで見た(2014年11月25日の日記参照)ときも、鳴り始めが美しいSinc関数になっていて、波形の打ち切りなどもありませんでした。基本に忠実&作りが良いのでしょう。
何も考えないで適当に買ったんですけど、買って良かった一品でした。
私にはハイレゾは意味がありませんね。聞こえないし、差もわかりません。
そもそもDAT(fs = 48kHz)とCD(44.1kHz)の差だってわかりませんし、もっといえばfs = 32kHzつまり16kHz以上の音を打ち切っても差がわかりません。
まあSin波だけデカい音量で鳴らせば16.5kHzくらいまでは聞こえますが、17kHz以降はもう何しても全然聞こえません。どう考えてもCD音質より上は要らないです……。
私にはハイレゾは意味ない(=わからない)というだけであって、ハイレゾ音源なんか買うな!とか、売るな!とか、そんなことは微塵も思っていません。
趣味の世界なんですから、楽しくやるのが一番です。差がわかろうがわかるまいが、死にゃあしねえし、外野の声なんて、気にしないで楽しむのが一番です。
メモ: 技術系の話はFacebookから転記しておくことにした。
目次: ベンチマーク
How is GNU `yes` so fast?を読んで。このサイトを Twitterで知って面白かったので、やってみました。
記事の最後にあるプログラム(iteration 4)のバッファサイズを8KBから16KBに変更するとGNU yesより速くなるようです。
家のLinuxマシンでやってみたらyesというかcoreutilsのバージョンによって全然速さが違うことがわかりました。
CPU : GNU yes 8.23 : GNU yes 8.26 : 自家製yes(バッファ16KB)
AMD A10 7800 : 123MiB/s : 2.8GiB/s : 3.2GiB/s
Intel Pentium J4205 : 40MiB/s : 1.9GiB/s : 2GiB/s
何も変わっていないように見えるyesも実は速度が進化したんですね…。
メモ: 技術系の話はFacebookから転記しておくことにした。
目次: ALSA
メモ代わりの超マニアックな話です。なぜALSAのドライバ、特にミキサーがregmapを使うのかを調べました。
ALSAのドライバでミュートのON/OFFとか、ボリューム上げ下げの機能を実装するときに、kcontrolというフレームワークを使います。
ボリュームはさらにもう一つ仕組みがあって面倒なので、ミュートON/OFFスイッチを例にします。下記はTexsas InstrumentsのTAS5711というDACのドライバです。
//from linux-4.4/sound/soc/codecs/tas571x.c
static const struct snd_kcontrol_new tas5711_controls[] = {
...
SOC_DOUBLE("Speaker Switch",
TAS571X_SOFT_MUTE_REG,
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
1, 1),
};
引数の意味はスイッチの名前(Speaker Switch)、レジスタのアドレス、Lチャネルを制御するときのシフト数、Rチャネルを制御するときのシフト数、最大値、反転するかどか、みたいです。
「反転するかどうか」を何に使っているかというと、通常ON = max値, OFF = min値という意味ですが、ON = min値, OFF = max値にするようです。TI TAS5711仕様書のSOFT MUTE REGISTER (0x06) を見た感じ、Muteするときは1設定しろとありますので、多分合っていると思います。間違ってたらごめんよ。
もちろんこれだけではダメで、struct snd_soc_codec_driverのcontrolsにポインタを代入し、snd_soc_register_codec() に渡す必要があります。
どうもtas571x.cのドライバはTAS5711とTAS5717という2つのDACに対応するため、渡し方がちょっと変わってるので、TAS5086用のドライバを見ます。
//from linux-4.4/sound/soc/codecs/tas5086.c
static const struct snd_kcontrol_new tas5086_controls[] = {
SOC_SINGLE_TLV("Master Playback Volume", TAS5086_MASTER_VOL,
...
static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
.probe = tas5086_probe,
.remove = tas5086_remove,
.suspend = tas5086_soc_suspend,
.resume = tas5086_soc_resume,
.controls = tas5086_controls, //★snd_kcontrol_new配列のポインタをここに渡している★
.num_controls = ARRAY_SIZE(tas5086_controls),
.dapm_widgets = tas5086_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets),
.dapm_routes = tas5086_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes),
};
static int tas5086_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
...
if (ret == 0)
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tas5086, //★snd_soc_codec_driverのポインタをここに渡している★
&tas5086_dai, 1);
return ret;
}
こんな感じです。本当に言いたかったことはregmapですけど、出てこないうちに終わってしまいました。また次回にします。
目次: ALSA
メモ代わりの超マニアックな話です。kcontrolについては前回を参照。
ALSAのkcontrolはレジスタのアドレスと値の範囲をALSAの共通レイヤに渡すだけで、ミキサー機能から、ハードウェアレジスタの値が書き換えられます。レジスタの値をアクセスする関数を一切書いていないにも関わらず、です。
最初、この仕組みがわからず、超不思議でした。どうなってるんでしょうか?
まずkcontrolが何をしているか見てみます。といってもkcontrol側は深追いせず、レジスタアクセスのみ調べます。ちなみにkcontrol_newの何がどうなってミキサーに繋がるか調べるのは、かなり大変そうです……。
//linux-4.4/sound/soc/codecs/tas571x.c
static const struct snd_kcontrol_new tas5717_controls[] = {
...
SOC_DOUBLE("Speaker Switch",
TAS571X_SOFT_MUTE_REG,
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
1, 1),
//linux-4.4/include/sound/soc.h
#define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ //★たぶんこれ使って読んでるんだろう★
.put = snd_soc_put_volsw, \
.private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
max, invert, 0) }
//linux-4.4/sound/soc/soc-ops.c
int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
...
ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
if (ret)
return ret;
...
//linux-4.4/sound/soc/soc-ops.c
static int snd_soc_read_signed(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int shift,
unsigned int sign_bit, int *signed_val)
{
...
ret = snd_soc_component_read(component, reg, &val);
if (ret < 0)
return ret;
//linux-4.4/sound/soc/soc-io.c
int snd_soc_component_read(struct snd_soc_component *component,
unsigned int reg, unsigned int *val)
{
int ret;
if (component->regmap)
ret = regmap_read(component->regmap, reg, val); //★regmapで読み出すか★
else if (component->read)
ret = component->read(component, reg, val); //★regmapが無くて、独自のレジスタアクセス関数があればそちらを使う★
else
ret = -EIO; //★どちらもなければエラー★
return ret;
}
はい、やっと出ましたregmapです。コードを見るとcomponent->regmapとやらを参照していますが、こんなものを設定した覚えはありません。どこから来たのでしょうか?
答えは前回の最後に呼んでいたsnd_soc_register_codec() にあります。
答えはsnd_soc_register_codec() にある、あるんですけど、ちょっと面倒くさいです。
//linux-4.4/sound/soc/soc-core.c
int snd_soc_register_codec(struct device *dev,
const struct snd_soc_codec_driver *codec_drv,
struct snd_soc_dai_driver *dai_drv,
int num_dai)
{
...
struct snd_soc_codec *codec;
...
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
codec->component.codec = codec;
ret = snd_soc_component_initialize(&codec->component, //★注目その2★
&codec_drv->component_driver, dev);
if (ret)
goto err_free;
...
mutex_lock(&client_mutex);
snd_soc_component_add_unlocked(&codec->component); //★注目その1★
list_add(&codec->list, &codec_list);
mutex_unlock(&client_mutex);
...
順序が逆転しますが、ゴールから逆にたどる形で説明します。まずcomponent->regmapがどこから来たのか?を追います。答えはsnd_soc_component_add_unlocked() にあります。
//linux-4.4/sound/soc/soc-core.c
static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
{
if (!component->write && !component->read) {
if (!component->regmap)
component->regmap = dev_get_regmap(component->dev, NULL);
...
//linux-4.4/drivers/base/regmap/regmap.c
struct regmap *dev_get_regmap(struct device *dev, const char *name)
{
struct regmap **r = devres_find(dev, dev_get_regmap_release,
dev_get_regmap_match, (void *)name);
...
//linux-4.4/drivers/base/regmap/regmap.c
static int dev_get_regmap_match(struct device *dev, void *res, void *data)
{
struct regmap **r = res;
...
/* If the user didn't specify a name match any */
if (data)
return (*r)->name == data;
else
return 1;
...
//linux-4.4/drivers/base/devres.c
void * devres_find(struct device *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
struct devres *dr;
...
dr = find_dr(dev, release, match, match_data);
...
//linux-4.4/drivers/base/devres.c
static struct devres *find_dr(struct device *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
struct devres_node *node;
list_for_each_entry_reverse(node, &dev->devres_head, entry) {
struct devres *dr = container_of(node, struct devres, node);
if (node->release != release)
continue;
//★今回はmatchにdev_get_regmap_match() を渡しています★
//★またmatch_dataにはNULLを渡しています★
//★dev_get_regmap_match() の第3引数がNULLの場合、最初に見つけたregmapにヒットします★
if (match && !match(dev, dr->data, match_data))
continue;
return dr;
}
return NULL;
}
以上のようにcomponent->regmapはdev_get_regmap(component->dev, NULL) の結果を使っています。dev_get_regmap() の第2引数NULLなので、デバイスのリソース一覧(dev->devres_head)から、とにかく何でも良いので最初に見つけたregmapを返してくれという意味になります。
最初見たとき、関数名と中身が全然違うので、分かるわけねぇだろこんなの…って思いました。
次にcomponent->devはどこから来たのか?を追います。答えはsnd_soc_component_initialize() にあります。
//linux-4.4/sound/soc/soc-core.c
static int snd_soc_component_initialize(struct snd_soc_component *component,
const struct snd_soc_component_driver *driver, struct device *dev)
{
...
component->dev = dev; //★これです★
component->driver = driver;
component->probe = component->driver->probe;
component->remove = component->driver->remove;
こちらはそんなに難しくないですね。
ここまで調べてきて分かったことは、下記の通りです。
ここで最後の疑問です。regmapをdevresに追加する方法は?わからないですね。追加した覚えはありません。
でも絶対何かしているハズです。もう一回TAS5086のドライバに戻ってみます。
//linux-4.4/sound/soc/codecs/tas5086.c
static int tas5086_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
...
priv->regmap = devm_regmap_init(dev, NULL, i2c, &tas5086_regmap); //★もしかしてこれ?★
if (IS_ERR(priv->regmap)) {
ret = PTR_ERR(priv->regmap);
dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
return ret;
}
...
if (ret == 0)
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tas5086, //★snd_soc_codec_driverのポインタをここに渡している★
&tas5086_dai, 1);
return ret;
}
ざっと見てもregmapに何かしているのはdevm_regmap_init() だけです。どうしてdevm_regmap_init() なのでしょうか?どうしてregmap_init() ではダメなのでしょうか?
//linux-4.4/include/linux/regmap.h
#define devm_regmap_init(dev, bus, bus_context, config) \
__regmap_lockdep_wrapper(__devm_regmap_init, #config, \
dev, bus, bus_context, config)
//★__regmap_lockdep_wrapper() はロックを取って __devm_regmap_init() を呼ぶマクロ★
//linux-4.4/drivers/base/regmap/regmap.c
struct regmap *__devm_regmap_init(struct device *dev,
const struct regmap_bus *bus,
void *bus_context,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap **ptr, *regmap;
ptr = devres_alloc(devm_regmap_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
//★regmap_init() も結局 __regmap_init() を呼ぶだけなのに何が違う?★
regmap = __regmap_init(dev, bus, bus_context, config,
lock_key, lock_name);
if (!IS_ERR(regmap)) {
*ptr = regmap;
devres_add(dev, ptr); //★もしかしてこれ?★
} else {
devres_free(ptr);
}
return regmap;
}
//linux-4.4/drivers/base/devres.c
void devres_add(struct device *dev, void *res)
{
struct devres *dr = container_of(res, struct devres, data);
unsigned long flags;
spin_lock_irqsave(&dev->devres_lock, flags);
add_dr(dev, &dr->node);
spin_unlock_irqrestore(&dev->devres_lock, flags);
}
//linux-4.4/drivers/base/devres.c
static void add_dr(struct device *dev, struct devres_node *node)
{
devres_log(dev, node, "ADD");
BUG_ON(!list_empty(&node->entry));
list_add_tail(&node->entry, &dev->devres_head); //★ここでリストに追加している!★
}
以上のように、devm_regmap_init() もregmap_init() もregmap初期化という役割から見ると同等ですが、devm_regmap_init() はデバイスのリソースリスト(dev->devres_head)にregmapを登録するという役割も果たしています。
まとめると、
このルールだけ見れば難しくないですが、なぜこのルールなのか調べると、結構複雑でした。面白かったです。
サーバのexim4がIPv6 socket creation failedというエラーを出し続けていて、鬱陶しいです。
どうしたものかと思っていたら、同じ症状で悩んでいる人を見つけました。要は設定ファイルにdisable_ipv6 = trueを足せば良いだけみたいですが、Debianのexim4は変な設定方法になっていて苦戦していました。
まずは、現在の状態を確認してみます。
# update-exim4.conf # exim4 -bP disable_ipv6 no_disable_ipv6
これは無効(disable)が無効(no)なので、IPv6有効状態です。なんでenable_ipv6じゃないんだろう。変な名前…。
変更方法ですが、先ほどのフォーラムによれば /etc/exim4/update-exim4.conf.confに足しても意味が無くて/etc/exim4/exim4.conf.templateに足すと良いみたいです。
# vim /etc/exim4/exim4.conf.template # update-exim4.conf # exim4 -bP disable_ipv6 disable_ipv6
とりあえず設定ファイルの先頭に足してみたら効いているみたいですが、どこでも良いのか?これ。
一応 /usr/share/doc/exim4/README.Debian.gzにマニュアルがありますが、クッソ長くて読む気が起きないです。
拾い読みした感じだと、多分2.1.1.12. Split configuration into small filesの真ん中辺りに書いてある、
If you chose unsplit configuration, update-exim4.conf builds the configuration from /etc/exim4/exim4.conf.template, which is basically the files from /etc/exim4/conf.d/ concatenated together at package build time, and thus guarantees consistency on the target system.
設定ファイルを分割していない人(私がそう)はexim4.conf.templateに全ての設定が書いてあるから、何か足したければexim4.conf.templateに足せば良さそうだな?ってのが何となくわかります。でも、仕組みは良いから使い方を書いてくれないかな…って気分になりますね。
< | 2017 | > | ||||
<< | < | 06 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | - |
合計:
本日: