Minim:快速入门指南

欲开始使用Minim,首先你得建立一个Minim对象,这样你便能载入音频文件或者建立声音输入与输出。而后,在你退出程序前,你必须关闭你从Minim获得的所有I/O类然后关闭你的Minim范本。音频输入/输出类包括AudioPlayer, AudioSample, AudioSnippet, AudioInput, 和 AudioOutput。达到这个目的的一个很好的方式就是在你关闭所有音频类的位置定义一个停止(stop)方法(method)。以下是以上几点的一个范例(部分):

Minim minim;
AudioPlayer player;
AudioInput input;

void setup()
{
  size(100, 100);

  minim = new Minim(this);
  player = minim.loadFile("song.mp3");
  input = minim.getLineIn();
}

void draw()
{
  // 做你想做的
}

void stop()
{
  // 你从Minim.loadFile()获得的音频播放器
  player.close();
  // 你从Minim.getLineIn()获得的音频输入
  input.close();
  minim.stop();

  // 这些称作停止方法(stop method) 
  // you are overriding by defining your own
  // 它必须被呼叫用以完成你的程常规的清理工作 
  super.stop();
}

Ess及Sonia的用户应当对这些协定非常熟悉。做所有这些是因为全部音频输入/输出都在独立的线路工作,并且他们必须被允许以常规的方式结束。你同样可以在执行你程序的时候关闭一个音频类以释放它的资源。

播放一个音频文件

写Minim一个最主要的动力来源于没有哪个Processing的库可以支持音频文件立体声回放。而在Minim里边,播放一个音频文件将不是一般的简单。仅需将你的音频文件放到sketch文件夹的data文件夹内,然后运行如下代码:

import ddf.minim.*;

Minim minim;
AudioPlayer song;

void setup()
{
  size(100, 100);

  minim = new Minim(this);

  // 这将载入data文件夹内的文件mysong.wav
  song = minim.loadFile("mysong.wav");
  song.play();
}

void draw()
{
  background(0);
}

void stop()
{
  song.close();
  minim.stop();

  super.stop();

Minim可以播放一切典型的未压缩的音频档,如WAV, AIFF, 和AU。通过Javazoom的MP3SPI的支持,它也具备了MP3文件的播放功能。

找回Meta数据

Meta数据是一个文件的信息,与文件的实际内容不同。你可以在将文件载入AudioPlayer之后获取一个文件的meta数据。你这么做最大的原因可能是获得文件的ID3标签信息。以下代码能替你做到这点:

import ddf.minim.*;

Minim minim;
AudioPlayer groove;
AudioMetaData meta;

void setup()
{
  size(512, 256, P3D);

  minim = new Minim(this);
  // groove.mp3应位于data文件夹下
  groove = minim.loadFile("groove.mp3");
  meta = groove.getMetaData();

  // serif.vlw应位于data文件夹下,vlw是p5字体文件并由p5本身创建
  textMode(SCREEN);
}

int ys = 15;
int yi = 15;

void draw()
{
  background(0);
  int y = ys;
  text("File Name: " + meta.fileName(), 5, y);
  text("Length (in milliseconds): " + meta.length(), 5, y+=yi);
  text("Title: " + meta.title(), 5, y+=yi);
  text("Author: " + meta.author(), 5, y+=yi);
  text("Album: " + meta.album(), 5, y+=yi);
  text("Date: " + meta.date(), 5, y+=yi);
  text("Comment: " + meta.comment(), 5, y+=yi);
  text("Track: " + meta.track(), 5, y+=yi);
  text("Genre: " + meta.genre(), 5, y+=yi);
  text("Copyright: " + meta.copyright(), 5, y+=yi);
  text("Disc: " + meta.disc(), 5, y+=yi);
  text("Composer: " + meta.composer(), 5, y+=yi);
  text("Orchestra: " + meta.orchestra(), 5, y+=yi);
  text("Publisher: " + meta.publisher(), 5, y+=yi);
  text("Encoded: " + meta.encoded(), 5, y+=yi);
}

void stop()
{
  // 日完之后,永远记得关闭Minim的音频类
  groove.close();
  // 永远记得在退出前关闭Minim
  minim.stop();

  super.stop();
}

绘制一个波形

当然你会想要将你正在播放的声音绘制出来。Minim中所有操控音频数据输入输出的类(除了AudioSnippet)都来源于一个被称为AudioSource的类。这个类定义了三个AudioBuffer成员,他们将继承扩展AudioSource的类们。因此你可以通过AudioPlayer对象进入缓冲器(buffer)。缓冲器被命名为左、右和混合。它们分别包含左通道、右通道和左右混合通道。即使你播放的音频文件是单声道的,三个缓冲器仍然可用并会返回值。当你在播放一个单声道音频文件的时候,你会发现三个缓冲器内都包含同样的信息。ok,让我们来画一个波形吧,我们可以如此重定义draw函数:

void draw()
{
  background(0);
  stroke(255);
  // 我们通过将相邻的值连成一条线来绘制波形
  // 我们将每一个值乘以50 
  // 因为缓冲器里的值已经过标准化
  // 这意味着他们的值是从-1到1。 
  // 如果我们不将他们扩大
  // 我们的波形看上去将会更像是一条直线。
  for(int i = 0; i < song.bufferSize() - 1; i++)
  {
    line(i, 50 + song.left.get(i)*50, i+1, 50 + song.left.get(i+1)*50);
    line(i, 150 + song.right.get(i)*50, i+1, 150 + song.right.get(i+1)*50);
  }
}

这段绘制代码将持续起效,无论song类的输入、输出是什么类型,因为他们都扩充了AudioSource,而AudioSource提供了缓冲器。我们现在碰到两个问题,运行窗口为100*100,太小,然后我们还不知道缓冲器的长度(比如说,song.bufferSize() 返回什么值)。第一个问题很容易解决,我们可以设置窗口尺寸为512*200。至于第二个问题,我们可以在呼叫loadFile的时候包含我们想要的缓冲器的长度。因此,在setup里应该这么写:

void setup()
{
  size(512, 200);

  minim = new Minim(this);

  // 指定512为采样缓冲器sample buffers)的长度
  // 默认缓冲器大小是1024
  song = minim.loadFile("mysong.wav", 512);
  song.play();
}

这保证了在荧幕实时输出缓冲器里的值的时候让他们保持统一。如果你不指定缓冲器长度,则1024为其默认值。

绘制一个频谱

另外,你可能还想要在播放你的歌曲的时候分析它们并借此绘制其频谱。你可以使用一个FFT对象来做到这一点。如果我们在我们的程序内包含一个FFT对象并在波形背后稍微淡化的绘制频谱,我们的程序看起来将像这个样子:

import ddf.minim.*;
import ddf.minim.analysis.*;

Minim minim;
AudioPlayer song;
FFT fft;

void setup()
{
  size(512, 200);

  // 总是首先开启Minim!
  minim = new Minim(this);
// 指定512为采样缓冲器sample buffers)的长度
  // 默认缓冲器大小是1024
song = minim.loadFile("mysong.wav", 512);
song.play();
// 一个FFT需要知道它要分析的缓冲器的长度
// 同时需要知道它正在分析的音频文件的采样率
fft = new FFT(song.bufferSize(), song.sampleRate());
}

void draw()
{
background(0);
// 首先在song的缓冲器之一前置一个fft
// 我正使用的是mix缓冲器
// 当然,你可以选择任何你喜欢的
 fft.forward(song.mix);

stroke(255, 0, 0, 128);
// 用一系列垂直线绘制频谱
// 我将getBand的值乘以4 
// 这样,我们将能更好的观测频谱
for(int i = 0; i < fft.specSize(); i++)
{
line(i, height, i, height - fft.getBand(i)*4);
}

stroke(255);
// 我们通过将相邻的值连成一条线来绘制波形
  // 我们将每一个值乘以50 
  // 因为缓冲器里的值已经过标准化
  // 这意味着他们的值是从-1到1。 
  // 如果我们不将他们扩大
  // 我们的波形看上去将会更像是一条直线。
for(int i = 0; i < song.left.size() - 1; i++)
{
line(i, 50 + song.left.get(i)*50, i+1, 50 + song.left.get(i+1)*50);
line(i, 150 + song.right.get(i)*50, i+1, 150 + song.right.get(i+1)*50);
}
}

void stop()
{
song.close();
minim.stop();

super.stop();
}

合成声音

最后,是关于Minim内可用的振荡器和滤波器类的简介。在合成声音之前,你需要问Minim要一个AudioOutput:

AudioOutput out = minim.getLineOut();

无引数呼叫getLineOut将返回一个缓冲器大小为1024、深度16bit、采样率44100 Hz的立体声线性输出。两件事你可能会考虑到:如果你需要单声道线性输出或不同的缓冲器大小,你可以这样呼叫getLineOut:

AudioOutput out = minim.getLineOut(Minim.MONO);

或者

AudioOutput out = minim.getLineOut(Minim.STEREO, 512);

这里512是你所希望的缓冲器长度。一旦你有了一个音频输出(AudioOutput),你便可以将音频信号(AudioSignals)和音频效果(AudioEffects)附加其上。Minim为你配备了为数不多的几个供你使用。ok,现在我们就在音频输出上加一个方波(SquareWave)和低通滤波器(LowPassSP)试试:

import ddf.minim.*;
import ddf.minim.signals.*;
import ddf.minim.effects.*;

Minim minim;
AudioOutput out;
SquareWave square;
LowPassSP   lowpass;

void setup()
{
  size(512, 200);

  minim = new Minim(this);

  // 获取一个缓冲器为512的立体声线性输出
  out = minim.getLineOut(Minim.STEREO, 512);

  // 创建一个频率为440 Hz的方波, 
  // 振幅为1,采样率为44100 Hz
  square = new SquareWave(440, 1, 44100);

  // 创建一个截取频率为200 Hz的低通滤波器 
  // 采样率为44100 Hz
  lowpass = new LowPassSP(200, 44100);

  // 现在我们便可将方波和滤波器搭载到我们的输出上
  out.addSignal(square);
  out.addEffect(lowpass);
}

void draw()
{
  // 你可能想要在这里绘制波形或做其他你想干的事
}

// 这儿我们提供了一个静音的方式
// 因为,让我们面对它吧,这并非一个令人愉悦的声音
void keyPressed()
{
  if ( key == 'm' )
  {
    if ( out.isMuted() )
    {
      out.unmute();
    }
    else
    {
      out.mute();
    }
  }
}

void stop()
{
  out.close();
  minim.stop();

  super.stop();
}

这便是快速上手指南,这里并没有列出所有Minim的功能(当然以后会)。你可以自行阅读javadoc来了解所有的API。
另外,你还可以通过浏览在线范例来从不同方面了解Minim的全部功能。同时,所有的在线范例都包含在完整版本的Minim库内,你可以通过processing界面的File > Examples > minim找到并演示它们。

Be Sociable, Share!

《Minim:快速入门指南》有4个想法

  1. 好像这一页的文字跟网页代码混乱了,显示的都是网页的代码

    Reply

    ww Reply:

    @DC, 已修正,感谢指出。

    Reply

  2. 请问我已经在自己的sketch里加入了声音,但是我想让画面跟随音乐的停止或暂停而静止,应该怎么写呢,我用if(song.stop()){…}不行,当然我知道这个语法是错的,请教正确的语法

    Reply

    ww Reply:

    如你所见,我至今并未深入研究这个库。我想你可以到processing官网提问以获取答案。

    Reply

发表评论

电子邮件地址不会被公开。 必填项已用*标注