为什么这个c语言代码会打印3个Hi! ,4个Bye! ?

30行代码,每行字符150,如果乘一下是4500个字符也就是4.5k,说句实话已经相当奢侈了,奢侈到足矣塞下一套完整的声码器模型外加一首的的古筝版《穿越时空的思念》了。

观众朋友可能就要问了,诶为什么是古筝版本的不是钢琴版本的呢?那当然是古筝的声学建模比钢琴简单的多,实际上拨弦乐大多比击弦乐建模难度差的不是一个级别的,如果你想了解钢琴声学建模的声码器模型,最近我刚好在整这个:

但如果你只是想用30行代码装个逼,诶,那这篇文章你就来对地方了,因为我不仅要装逼给你看,也会告诉你如何装这个逼,那观众朋友可能又要问了,为什么是《穿越时空的思念》而不是更让人鸡血沸腾而且更装逼的《克罗地亚狂想曲》呢,那当然是因为4.5k的体量说多也不多说少也不少,我们不仅要塞下整个声学结构的代码,还要把整首曲子也塞进去,而实际真正码起来的话,我们要给自己留一点空间,比如我们可能4.5k代码的体量,我们最终能用到的,大约是3.5k左右,考虑到写这种娱乐性质的回答真正花的时间也不能太多,于是,《穿越时空的思念》这种又简单又好听又方便摸鱼的曲目自然是首选。

最后,“talk is cheap,show me the code”仍然是一句恒古不变的真理,因此在下面我教大家如何写这种装逼代码之前,我们先把最终的代码贴上来,并让大家康康最终的运行结果是什么

怎么样,听上去还行是不是当然,整个项目最终使用了24行代码,一共3058个字符,也就是3k左右的空间,当然最终最终限制行数限制字数的“编程比赛”更多是为了装逼用的,工程代码如果写成这个样子,在我们这里肯定是要被拉去浸猪笼的。

那么,这个项目到底是怎么出来的呢,显然,如果我们一开始就写这种代码,不仅看的人难受,写的人也难受,因此在完成装逼版本代码之前,我们要先写个“说人话”版本的代码。

下面的,就是这个压缩后代码的原始版本

现在让我们一步一步来,详解这个代码到底是怎么出来的。

Q1:我们怎么保存乐谱

对于怎么把资源封装在代码里,这里我们显然不讨论那种编译器相关的打包方式,那种办法毕竟移植性太低,而且封装外部资源,显然在规则里不被允许,因此,我们可以使用一个静态数组的形式来存储资源,比如

这种方式,但这种方式在这种限制大小的代码中很蠢,因为你发现一个字节要用4-5个字节在存储,空间整整膨胀了5倍.因此这种方式显然不太合适,当然我们可以改进一下使用类似于这种

这种方式,这需要额外编写解码代码将十六进制数据转换为数据流,但显然比之前那种好的多,但是虽然好,却不够好,因为你发现,一个字节仍然需要2个字节进行存储,而一首简单的《穿越时空的思念》的mid文件,最小的都有2k多,如果这样存储的话,我们几乎就没有额外的空间来写代码了。

不过既然都用了ASC文本保存了,那为什么不选用更好一点的BASE64编码呢,虽然它仍然会导致空间轻微膨胀,但无疑是相当不错的一种编码方式了,并且显然我们只需要解码代码,而编码那一部分我们可以用别的程序去做,随我们怎么折腾,因此看上面中下面这段代码,实际就是BASE64的解码代码

而我们发现,穿越时空的思念的mid文件已经达到了2.68kb

显然是不可接受的,因此我们需要对midi文件进行进一步的裁剪,对于key up,key velocity和一些其它的附加信息,显然我们不需要,time stamp可以进一步裁剪压缩到1-2字节,剩下就只需要1字节的note信息了,去掉这类信息后,再经过base64编码,我们最终得到了一个长度为849字节的曲谱信息

对于这类限制字数的代码,弦乐的数学建模一定要简单简单再简单,因此,几乎是生理本能的我们想起了著名的Karplus-Strong弦模型公式,

Karplus-Strong 是一个弦乐(吉他,钢琴,古筝.....)的物理(数学)模型,算法简单但生成的音效却很自然.

为了便于解释其原理,笔者对算法的说明与具体实现做了一些修改

1.首先生成一段白噪声,量化后生成一段离散样本信号X,长度为N.

2.将离散样本信号的首尾相加取平均,乘以一个衰减系数alpha(大多取一个略小于1.0的值比如0.995),将结果赋值给X[0].

(0<i<N)的方式,处理整个白噪声信号,这样就生成了第一个基音周期的样本信号,将这个样本信号拷贝到输出信号中.

4.重复第二第三步继续对样本信号处理,直到生成到需要的长度为止,那么,我们就得到了一个由基音构成的多个泛音组成的谐波信号,就得到了弦乐中的一个音.

算法很简单,也非常的巧妙,下面就来进一步究其原理,搞清楚为什么这么干.

那么,弦乐信号是什么样的呢,以SoundLab为例,我们分析一段钢琴演奏

Q2.1:为什么使用白噪声作为初始激励信号

首先第一点,我们注意到在每一段琴音的开始阶段,有一条频域范围很宽的亮线

这是一个典型的击打乐造成的冲击信号的频域特性,代表着钢琴琴锤击打在琴弦上那一瞬间的声音能量,这也解释了Karplus-Strong为什么使用白噪声作为了初始信号(白噪声的频域特性也就是这个样子的),之后琴弦震动,配合音板特性,形成了一系列泛音.

Q2.2:为什么将信号两个值相加取平均

这是Karplus-Strong算法非常巧妙的一步,我们知道离散信号取平均相当于在加了一个低通滤波器,而从频域图我们可以看到,高频信号的能量衰减,随着时间要比低频的能量衰减快得多

因此,当我们循环迭代取平均时,实际上就是不断地让高频能量进行衰减,以模拟弦音的这种特性

这个很好理解,能量本来就是不断衰减的,可以看到在第一个冲击能量引入后,时域能量呈现不断衰减的特性.

Q2.4:泛音是如何形成的

当我们按照算法的2,3,4步不断去构造基音信号并将他们拼接起来,就形成了一个以基音周期为基准的泛音信号.从而最终得到一个音的信号

那么,最终的代码实际上就是下面这一些

  1. 首先,我们先使用一个素数同余随机数发生代码,也就是上面的函数rd(),你可能要问了,为什么不使用“math.h”里的rand函数呢,当然是因为#include “math.h”要占用整整一行代码,奢侈浪费啊。
  2. trigger后,即初始化了一个karplus-strong的滤波系统,之后我们就可以从这个滤波器中构建PCM音频流序列了。

Q3:混音系统如何实现

有了上面的karplus-strong滤波器后,我们就可以用他们组成一个滤波器系统了,在上面的代码中,我使用了32个滤波器组,对输出求卷积后,即可取得最终的音频序列了,这部分的代码比较简单,如下所示

最后,我们使用这个系统合成了最终的PCM码流,剩下就是输出这个码流到音频设备中播放了,当然这个是平台相关的代码,在上面的代码中,笔者使用了DirectSound来播放最终的音频流,也就是下面这部分的代码

当然,这部分的代码是平台相关的,你完全可以把上面的代码删掉,那么就可以把这个代码移植到linux android ios上,至于是导出为wav文件还是使用其它音频接口来播放,完全木有问题,这就看你个人喜好了。

最后,我们将上面的代码压缩一下,例如把变量或函数名变短,每行控制在150个字符,就得到最终的代码了,我们甚至还有1000字左右的盈余,对于这部分,如果你有兴趣你可以塞进一个豪华版本的《穿越时空的思念》,当然你也可以再拉上几个双二阶滤波器组个EQ或对soundboard进行建模,让最终的声音变得更好听一点,当然这部分我就不进一步讨论了,你可以翻阅PainterEngine的声学相关代码或者相关论文,提高更多的姿势水平。最后放上彩色版代码贴图

是不是非常的Geek艺术风

}

前三问太简单不说了,第四问把反汇编出来的机器码放到main这个符号的位置就行了(其实unix下还可以不让用main,看别人知不知道_start),实现在 或者 的答案里有,就不贴了,这不是我想说的重点。以下跑个题。

这类题目显然不是给一般的『初学编程/初学C语言』的人做的,我也绝不会在正规的软件开发里这么写,但这并不意味着它们就没有价值

事实上,我个人非常喜欢做这类题目。hack各种编译器的实现,给写代码加上各种限制之后突破重重限制最后达成目标,这本来就是一种挑战自我的游戏。只要你不在正式场合使用,哪怕不是为了研究二进制安全,谁又规定了写代码非得是要『有用』呢?好比为什么有人要登顶珠峰呢?为什么有人愿意去想哥特巴赫猜想呢?因为好玩,因为我能。每次『哇,还能这么玩,厉害厉害』的感觉都让我很爽,这就够了啊。

自然,大多数人不会觉得有趣,那走开便是,没必要嘲讽吧。

分享一些相关的网站、题库和比赛:

1. 中有一个分类是C++ traps,不过题目不多。其中的题目例如:

要求你在/*INPUT*/处填上一行不超过12个字符的代码(有些字符/字符组合禁止使用),使得victory()函数最终被调用。Hint:这网站用的g++版本(Ubuntu 4.8.2-19ubuntu1)里,f+10也是一个表达式哟。

里有几百道题(组成了一个大地图,本质上是个有向无环图,某些题只有做对了别的题才能解锁)。这些题目中,有的需要密码学知识,有的需要广博的知识面,有的需要大开脑洞,有的需要逆向工程,有的要你会图像识别/语音识别,有的需要你写一个游戏外挂...其中针对某些题目定义了一种基于栈的语言(及其二维加强版),然后在这个语言上加诸多限制,例如『用不超过X个字符的代码打印出hello world/实现排序』『在不使用减法/除法/比较运算的前提下输出两个数中较大者』『写一个打印自己源代码本身的程序』等等。

3. 印度有个叫IIIT, Hyderabad的学校,有个每年一度的『技术文化节』叫Felicity。通常每年的Felicity其中都有一个面向全世界的线上比赛叫做TLE(Time Limit Exceeded),为期24小时。借用一下 :

Time Limit Exceeded是一个另类的C/C++编程挑战赛,得分的依据千奇百怪,源码的长度、分号的个数、空格的个数都有可能算到得分公式里。例如,写一个Hello World程序,得分是100/(1 + 分号个数 + 空格个数)。下面两段代码中,第一个代码用了四个空格和一个分号,因此只能得16.67分;第二个代码只用到一个空格,因此可以得到50分。

还有一些诸如这样的比赛,知名度就比较高了,就不细说了。IPSC我印象中某年有一道题,题目描述完全是空白的,参赛者需要通过反复提交程序的方式,利用不同的出错信息(运行时错误,超时,爆内存等等)来枚举出输入数据的内容,从而猜出题目要干啥。当然是要写个程序来自动化这件事啦。

与出题者斗,其乐无穷。

}

四年级英语下册期末测试卷3

[版权声明] 本站所有资料由用户提供并上传,若内容存在侵权,请联系邮箱。资料中的图片、字体、音乐等需版权方额外授权,请谨慎使用。网站中党政主题相关内容(国旗、国徽、党徽)仅限个人学习分享使用,禁止广告使用和商用。

}

我要回帖

更多关于 c语言心形代码 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信