/* * VorbisEncoder.java */ import java.io.IOException; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import java.util.Random; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; import org.tritonus.lowlevel.ogg.StreamState; import org.tritonus.lowlevel.ogg.Packet; import org.tritonus.lowlevel.ogg.Page; import org.tritonus.lowlevel.vorbis.Block; import org.tritonus.lowlevel.vorbis.Comment; import org.tritonus.lowlevel.vorbis.DspState; import org.tritonus.lowlevel.vorbis.Info; public class VorbisEncoder { private static final int READ = 1024; private static byte[] readbuffer = new byte[READ * 4 + 44]; /* out of the data segment, not the stack */ public static void main(String[] args) throws IOException { StreamState os = new StreamState(); /* take physical pages, weld into a logical stream of packets */ Page og = new Page(); /* one Ogg bitstream page. Vorbis packets are inside */ Packet op = new Packet(); /* one raw packet of data for decode */ Info vi = new Info(); /* struct that stores all the static vorbis bitstream settings */ Comment vc = new Comment(); /* struct that stores all the user comments */ DspState vd = new DspState(); /* central working state for the packet->PCM decoder */ Block vb = new Block(); /* local working space for packet->PCM decode */ boolean eos = false; int i; int founddata; /* we cheat on the WAV header; we just bypass 44 bytes and never verify that it matches 16bit/stereo/44.1kHz. This is just an example, after all. */ AudioInputStream ais = null; try { ais = AudioSystem.getAudioInputStream(new File(args[0])); } catch (UnsupportedAudioFileException e) { e.printStackTrace(); } AudioFormat format = ais.getFormat(); if (format.getChannels() != 2 || format.getSampleSizeInBits() != 16) { System.out.println("need 16 bit stereo!"); System.exit(1); } File outputFile = new File(args[1]); OutputStream output = new FileOutputStream(outputFile); /********** Encode setup ************/ /* choose an encoding mode */ /* (quality mode .4: 44kHz stereo coupled, roughly 128kbps VBR) */ vi.init(); vi.encodeInitVBR(format.getChannels(), (int) format.getSampleRate(), 0.1F); // max compression /* add a comment */ vc.init(); vc.addTag("ENCODER","encoder_example.c"); /* set up the analysis state and auxiliary encoding storage */ vd.initAnalysis(vi); vb.init(vd); /* set up our packet->stream encoder */ /* pick a random serial number; that way we can more likely build chained streams just by concatenation */ Random random = new Random(System.currentTimeMillis()); os.init(random.nextInt()); /* Vorbis streams begin with three headers; the initial header (with most of the codec setup parameters) which is mandated by the Ogg bitstream spec. The second header holds any comment fields. The third header holds the bitstream codebook. We merely need to make the headers, then pass them to libvorbis one at a time; libvorbis handles the additional Ogg bitstream constraints */ Packet header = new Packet(); Packet header_comm = new Packet(); Packet header_code = new Packet(); vd.headerOut(vc, header, header_comm, header_code); os.packetIn(header); /* automatically placed in its own page */ os.packetIn(header_comm); os.packetIn(header_code); /* We don't have to write out here, but doing so makes streaming * much easier, so we do, flushing ALL pages. This ensures the actual * audio data will start on a new page */ while (!eos) { int result = os.flush(og); if(result == 0) break; output.write(og.getHeader()); output.write(og.getBody()); } while (!eos) { int bytes = ais.read(readbuffer, 0, READ * 4); /* stereo hardwired here */ if (bytes == 0 || bytes == -1) { /* end of file. this can be done implicitly in the mainline, but it's easier to see here in non-clever fashion. Tell the library we're at end of stream so that it can handle the last frame and mark end of stream in the output properly */ vd.write(null, 0); } else { /* data to encode */ /* expose the buffer to submit data */ float[][] buffer = new float[format.getChannels()][READ]; // float[][] buffer = vd.buffer(READ); /* uninterleave samples */ for (i = 0;i < bytes/4;i++) { int nSample; float fSample; nSample = (readbuffer[i * 4 + 1] << 8) | (0x00ff & readbuffer[i * 4 + 0]); fSample = nSample / 32768.0F; buffer[0][i] = fSample; nSample = (readbuffer[i * 4 + 3] << 8) | (0x00ff & readbuffer[i * 4 + 2]); fSample = nSample / 32768.f; buffer[1][i] = fSample; } /* tell the library how much we actually submitted */ vd.write(buffer, bytes/4); } /* vorbis does some data preanalysis, then divvies up blocks for more involved (potentially parallel) processing. Get a single block for encoding now */ while (vd.blockOut(vb) == 1) { /* analysis, assume we want to use bitrate management */ vb.analysis(null); vb.addBlock(); while (vd.flushPacket(op) != 0) { /* weld the packet into the bitstream */ os.packetIn(op); /* write out pages (if any) */ while (!eos) { int result = os.pageOut(og); if(result == 0) break; output.write(og.getHeader()); output.write(og.getBody()); /* this could be set above, but for illustrative purposes, I do it here (to show that vorbis does know where the stream ends) */ if (og.isEos()) { eos = true; } } } } } /* clean up and exit. vorbis_info_clear() must be called last */ os.clear(); vb.clear(); vd.clear(); vc.clear(); vi.clear(); output.close(); /* ogg_page and ogg_packet structs always point to storage in libvorbis. They're never freed or manipulated directly */ // fprintf(stderr,"Done.\n"); // return(0); } } /*** VorbisEncoder.java ***/