使用文件和格式转换器

大多数处理声音的应用程序都需要读取声音文件或音频流。这是通用功能,而与程序随后对读取的数据(例如播放,混合或处理)无关。同样,许多程序需要编写声音文件(或流)。在某些情况下,已读取(或将要写入)的数据需要转换为其他格式。

正如访问音频系统资源中简要提到的那样,Java Sound API 为应用程序开发人员提供了用于文件 Importing/输出和格式转换的各种功能。应用程序可以在各种声音文件格式和音频数据格式之间进行读取,写入和转换。

采样包概述介绍了与声音文件和音频数据格式有关的主要类。作为 Comment:

此格式指定音频 samples 本身的排列方式,而不指定它们可能存储在其中的文件的结构。换句话说,AudioFormat描述“原始”音频数据,例如系统在catch程序后可能会处理您的程序从麦克风 Importing 或从声音文件中解析后。 AudioFormat包括诸如编码,字节 Sequences,通道数,采样率和每个采样的位数之类的信息。

Note:

Java Sound API 的实现不一定提供用于读取,写入和转换不同数据和文件格式的音频的全面功能。它可能仅支持最常见的数据和文件格式。但是,服务提供商可以开发和分发扩展该集合的转换服务,如您稍后将在提供音频采样服务中看到的。 AudioSystem类提供的方法使应用程序可以学习可用的转换,如稍后在转换文件和数据格式下所述。

读取声音文件

AudioSystem类提供两种类型的文件读取服务:

首先是getAudioFileFormat方法的三个变体:

static AudioFileFormat getAudioFileFormat (java.io.File file)
    static AudioFileFormat getAudioFileFormat(java.io.InputStream stream)
    static AudioFileFormat getAudioFileFormat (java.net.URL url)

如上所述,返回的AudioFileFormat对象告诉您文件类型,文件中数据的 Long 度,编码,字节 Sequences,通道数,采样率和每个采样的位数。

第二种文件读取功能由这些AudioSystem方法提供

static AudioInputStream getAudioInputStream (java.io.File file)
    static AudioInputStream getAudioInputStream (java.net.URL url)
    static AudioInputStream getAudioInputStream (java.io.InputStream stream)

这些方法为您提供一个对象(AudioInputStream),使您可以使用AudioInputStream的一种读取方法来读取文件的音频数据。我们将暂时看一个示例。

假设您正在编写一个声音编辑应用程序,该应用程序允许用户从文件中加载声音数据,显示相应的波形或频谱图,编辑声音,播放编辑后的数据并将结果保存到新文件中。也许您的程序将读取存储在文件中的数据,进行某种 signal 处理(例如在不改变音调的情况下放慢声音的算法),然后播放处理后的音频。无论哪种情况,都需要访问音频文件中包含的数据。假设您的程序为用户提供了选择或指定 Importing 声音文件的方式,那么读取该文件的音频数据涉及三个步骤:

以下代码段概述了这些步骤:

int totalFramesRead = 0;
File fileIn = new File(somePathName);
// somePathName is a pre-existing string whose value was
// based on a user selection.
try {
  AudioInputStream audioInputStream = 
    AudioSystem.getAudioInputStream(fileIn);
  int bytesPerFrame = 
    audioInputStream.getFormat().getFrameSize();
    if (bytesPerFrame == AudioSystem.NOT_SPECIFIED) {
    // some audio formats may have unspecified frame size
    // in that case we may read any amount of bytes
    bytesPerFrame = 1;
  } 
  // Set an arbitrary buffer size of 1024 frames.
  int numBytes = 1024 * bytesPerFrame; 
  byte[] audioBytes = new byte[numBytes];
  try {
    int numBytesRead = 0;
    int numFramesRead = 0;
    // Try to read numBytes bytes from the file.
    while ((numBytesRead = 
      audioInputStream.read(audioBytes)) != -1) {
      // Calculate the number of frames actually read.
      numFramesRead = numBytesRead / bytesPerFrame;
      totalFramesRead += numFramesRead;
      // Here, do something useful with the audio data that's 
      // now in the audioBytes array...
    }
  } catch (Exception ex) { 
    // Handle the error...
  }
} catch (Exception e) {
  // Handle the error...
}

让我们看看上面的代码示例中发生了什么。首先,外部 try 子句通过对AudioSystem.getAudioInputStream(File)方法的调用实例化AudioInputStream对象。此方法透明地执行所有必要的测试,以确定指定的文件是否实际上是 Java Sound API 支持的类型的声音文件。如果要检查的文件(在此示例中为fileIn)不是声音文件,或者不是某些不受支持的类型的声音文件,则将引发UnsupportedAudioFileException异常。此行为很方便,因为应用程序程序员无需为测试文件属性而烦恼,也不必遵循任何文件命名约定。而是,getAudioInputStream方法负责验证 Importing 文件所需的所有低级解析和验证。 外部try子句然后创建一个任意固定 Long 度的字节数组audioBytes。我们确保其 Long 度(以字节为单位)等于帧的整数,这样我们就不会final只读取一部分帧,甚至更糟的是仅读取一部分 samples。该字节数组将用作缓冲区,以临时保存从流中读取的音频数据块。如果我们知道只读取非常短的声音文件,那么我们就可以读取此文件,可以通过AudioInputStream's getFrameLength方法返回的帧 Long 度得出字节 Long 度,从而使该数组的 Long 度与文件中数据的 Long 度相同。 (实际上,我们可能只使用Clip对象.)但是在一般情况下,为了避免内存不足,我们改为分块读取文件,一次读取一个缓冲区。

内部try子句包含while循环,在此循环中,我们将AudioInputStream中的音频数据读取到字节数组中。您应该在此循环中添加代码,以适合程序需要的任何方式处理此数组中的音频数据。如果要对数据进行某种 signal 处理,则可能需要进一步查询AudioInputStream's AudioFormat,以了解每个 samples 的位数。

请注意,方法AudioInputStream.read(byte[])返回的是读取的*字节数,而不是 samples 或帧的数目。没有更多数据可读取时,此方法返回-1.一旦检测到这种情况,我们就从while循环中断。

写入声音文件

上一节介绍了使用AudioSystemAudioInputStream类的特定方法读取声音文件的基础。本节介绍如何将音频数据写出到新文件中。

以下AudioSystem方法创建指定文件类型的磁盘文件。该文件将包含指定的AudioInputStream中的音频数据:

static int write(AudioInputStream in, 
  AudioFileFormat.Type fileType, File out)

请注意,第二个参数必须是系统支持的文件类型之一(例如 AU,AIFF 或 WAV),否则write方法将抛出IllegalArgumentException。为避免这种情况,可以通过调用此AudioSystem方法来测试是否可以将特定的AudioInputStream写入特定类型的文件:

static boolean isFileTypeSupported
  (AudioFileFormat.Type fileType, AudioInputStream stream)

仅在支持特定组合的情况下才会返回true

通常,您可以通过调用以下AudioSystem方法之一来了解系统可以写入的文件类型:

static AudioFileFormat.Type[] getAudioFileTypes() 
static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream)

其中第一个返回系统可以写入的所有文件类型,第二个仅返回系统可以从给定的音频 Importing 流写入的文件类型。

以下摘录演示了一种使用上述write方法从AudioInputStream创建输出文件的技术。

File fileOut = new File(someNewPathName);
AudioFileFormat.Type fileType = fileFormat.getType();
if (AudioSystem.isFileTypeSupported(fileType, 
    audioInputStream)) {
  AudioSystem.write(audioInputStream, fileType, fileOut);
}

上面的第一条语句使用用户或程序指定的路径名创建一个新的File对象fileOut。第二条语句从先前存在的AudioFileFormat对象fileFormat获取文件类型,该对象可能是从另一个声音文件获得的,例如上面读取声音文件中读取的声音文件。 (您可以提供所需的任何受支持的文件类型,而不是从其他位置获取文件类型.例如,您可以删除第二条语句,并用AudioFileFormat.Type.WAVE替换上面代码中出现的其他fileType.)

第三条语句测试是否可以从所需的AudioInputStream写入指定类型的文件。像文件格式一样,此流可能来自先前读取的声音文件。 (如果是这样,大概是您已经以某种方式处理或更改了它的数据,因为否则有更简单的方法可以简单地复制文件.)或者流中可能包含从麦克风 Importing 中刚catch的字节。

最后,流,文件类型和输出文件将传递到AudioSystemwrite方法,以达到写入文件的 Object。

转换文件和数据格式

什么是格式化音频数据?回忆起,Java Sound API 区分音频文件格式和音频数据格式。两者或多或少是独立的。粗略地说,数据格式是指计算机表示每个原始数据点(samples)的方式,而文件格式是指存储在磁盘上的声音文件的组织。每种声音文件格式都有一个特定的结构,该结构定义了例如存储在文件头中的信息。在某些情况下,除了实际的“原始”音频 samples 外,文件格式还包括包含某种形式的元数据的结构。该页面的其余部分将检查 Java Sound API 的方法,这些方法支持各种文件格式和数据格式转换。

从一种文件格式转换为另一种文件格式

本节介绍了在 Java Sound API 中转换音频文件类型的基础。我们再次提出一个假设的程序,这次的 Object 是从任意 Importing 文件中读取音频数据,并将其写入类型为 AIFF 的文件中。当然,Importing 文件必须是系统能够读取的类型,而输出文件必须是系统能够写入的类型。 (在此示例中,我们假定系统能够写入 AIFF 文件.)示例程序不执行任何数据格式转换。如果 Importing 文件的数据格式不能表示为 AIFF 文件,则该程序只是通知用户该问题。另一方面,如果 Importing 的声音文件已经是 AIFF 文件,则程序会通知用户不需要进行转换。

以下函数实现了刚刚描述的逻辑:

public void ConvertFileToAIFF(String inputPath, 
  String outputPath) {
  AudioFileFormat inFileFormat;
  File inFile;
  File outFile;
  try {
    inFile = new File(inputPath);
    outFile = new File(outputPath);     
  } catch (NullPointerException ex) {
    System.out.println("Error: one of the 
      ConvertFileToAIFF" +" parameters is null!");
    return;
  }
  try {
    // query file type
    inFileFormat = AudioSystem.getAudioFileFormat(inFile);
    if (inFileFormat.getType() != AudioFileFormat.Type.AIFF) 
    {
      // inFile is not AIFF, so let's try to convert it.
      AudioInputStream inFileAIS = 
        AudioSystem.getAudioInputStream(inFile);
      inFileAIS.reset(); // rewind
      if (AudioSystem.isFileTypeSupported(
             AudioFileFormat.Type.AIFF, inFileAIS)) {
         // inFileAIS can be converted to AIFF. 
         // so write the AudioInputStream to the
         // output file.
         AudioSystem.write(inFileAIS,
           AudioFileFormat.Type.AIFF, outFile);
         System.out.println("Successfully made AIFF file, "
           + outFile.getPath() + ", from "
           + inFileFormat.getType() + " file, " +
           inFile.getPath() + ".");
         inFileAIS.close();
         return; // All done now
       } else
         System.out.println("Warning: AIFF conversion of " 
           + inFile.getPath()
           + " is not currently supported by AudioSystem.");
    } else
      System.out.println("Input file " + inFile.getPath() +
          " is AIFF." + " Conversion is unnecessary.");
  } catch (UnsupportedAudioFileException e) {
    System.out.println("Error: " + inFile.getPath()
        + " is not a supported audio file type!");
    return;
  } catch (IOException e) {
    System.out.println("Error: failure attempting to read " 
      + inFile.getPath() + "!");
    return;
  }
}

如前所述,此示例函数ConvertFileToAIFF的 Object 是查询 Importing 文件以确定它是否是 AIFF 声音文件,如果不是,则try将其转换为一个文件,从而生成路径名为的新副本。由第二个参数指定。 (作为练习,您可以try使此功能更通用,以便该功能不总是转换为 AIFF,而是转换为新的函数参数指定的文件类型.)请注意,副本的音频数据格式-即,新文件模仿原始 Importing 文件的音频数据格式。

此功能大部分是不言自明的,并不特定于 Java Sound API。但是,例程使用了一些 Java Sound API 方法,这些方法对于声音文件类型的转换至关重要。这些方法调用都可以在上面的第二个try子句中找到,并包括以下内容:

这些方法中的第二个isFileTypeSupported有助于在写入之前确定是否可以将特定的 Importing 声音文件转换为特定的输出声音文件类型。在下一部分中,我们将看到对该ConvertFileToAIFF示例例程进行一些修改后,如何转换音频数据格式以及声音文件类型。

在不同数据格式之间转换音频

上一节展示了如何使用 Java Sound API 将文件从一种文件格式(即一种声音文件)转换为另一种。本节探讨了一些支持音频数据格式转换的方法。

在上一节中,我们从任意类型的文件中读取数据,并将其保存在 AIFF 文件中。请注意,尽管我们更改了用于存储数据的文件的类型,但没有更改音频数据本身的格式。 (大多数常见的音频文件类型,包括 AIFF,都可以包含各种格式的音频数据.)因此,如果原始文件包含 CD 品质的音频数据(16 位 samples 大小,44.1 kHz 采样率和两个通道),则将我们的输出 AIFF 文件。

现在,假设我们要指定输出文件的* data *格式以及文件类型。例如,也许我们正在保存许多 Long 文件供 Internet 使用,并且担心文件所需的磁盘空间和下载时间。我们可能会选择创建包含较低分辨率数据的较小 AIFF 文件,例如,具有 8 位 samples 大小,8 kHz 采样率和单个通道的数据。

无需像以前那样深入讨论编码细节,让我们探讨一些用于数据格式转换的方法,并考虑为实现新目标而需要对ConvertFileToAIFF函数进行的修改。

音频数据转换的主要方法再次在AudioSystem类中找到。此方法是getAudioInputStream的变体:

AudioInputStream getAudioInputStream(AudioFormat
    format, AudioInputStream stream)

此函数返回AudioInputStream,这是使用指示的AudioFormatformat转换AudioInputStreamstream的结果。如果AudioSystem不支持该转换,则此函数将引发IllegalArgumentException

为避免这种情况,我们首先可以通过调用此AudioSystem方法来检查系统是否可以执行所需的转换:

boolean isConversionSupported(AudioFormat targetFormat,
    AudioFormat sourceFormat)

在这种情况下,我们将传递stream.getFormat()作为第二个参数。

要创建特定的AudioFormat对象,请使用以下所示的两个AudioFormat构造函数之一:

AudioFormat(float sampleRate, int sampleSizeInBits,
    int channels, boolean signed, boolean bigEndian)

它使用线性 PCM 编码和给定参数构造一个AudioFormat,或者:

AudioFormat(AudioFormat.Encoding encoding, 
    float sampleRate, int sampleSizeInBits, int channels,
    int frameSize, float frameRate, boolean bigEndian)

它也构造了一个AudioFormat,但是除了其他参数之外,您还可以指定编码,帧大小和帧速率。

现在,使用上述方法,让我们看看如何扩展ConvertFileToAIFF函数以执行所需的“低分辨率”音频数据格式转换。首先,我们将构造一个AudioFormat对象,该对象描述所需的输出音频数据格式。下面的语句就足够了,可以插入到函数顶部附近:

AudioFormat outDataFormat = new AudioFormat((float) 8000.0,
(int) 8, (int) 1, true, false);

由于上面的AudioFormat构造函数描述的是 8 位 samples 的格式,因此构造函数的最后一个参数无关紧要,该参数指定 samples 是大字节序还是小字节序。 (仅当 samples 大小大于单个字节时,大字节序与小字节序才是问题.)

以下示例显示了如何使用此新的AudioFormat转换从 Importing 文件创建的AudioInputStreaminFileAIS

AudioInputStream lowResAIS;         
  if (AudioSystem.isConversionSupported(outDataFormat,   
    inFileAIS.getFormat())) {
    lowResAIS = AudioSystem.getAudioInputStream
      (outDataFormat, inFileAIS);
  }

只要在构建inFileAIS之后,我们在哪里插入此代码都没关系。没有isConversionSupported测试,如果请求的特定转换不受支持,则调用将失败并引发IllegalArgumentException。 (在这种情况下,控制权将转移到函数中相应的catch子句中.)

因此,在此过程的这一点上,我们将产生一个新的AudioInputStream,这是由于将原始 Importing 文件(以AudioInputStream形式)转换为所需的低分辨率音频数据格式,如outDataFormat所定义。

生成所需的低分辨率 AIFF 声音文件的最后一步是用转换后的流lowResAIS替换对AudioSystem.write的调用中的AudioInputStream参数(即第一个参数),如下所示:

AudioSystem.write(lowResAIS, AudioFileFormat.Type.AIFF, 
  outFile);

对我们先前功能的一些修改产生了一些东西,可以转换音频数据和任何指定 Importing 文件的文件格式,当然前提是系统支持转换。

了解可以进行哪些转换

几种AudioSystem方法测试其参数以确定系统是否支持特定的数据格式转换或文件写入操作。 (通常,每个方法与另一个执行数据转换或写入文件的方法配对.)在我们的示例函数ConvertFileToAIFF中使用了其中一种查询方法AudioSystem.isFileTypeSupported,以确定系统是否能够将音频数据写入到其中。 AIFF 文件。相关的AudioSystem方法getAudioFileTypes(AudioInputStream)作为AudioFileFormat.Type实例的数组返回给定流支持的文件类型的完整列表。方法:BEGINCODE 布尔值 isConversionSupported(AudioFormat.Encoding 编码,
AudioFormat format)

首页