#lang scribble/doc @(require scribble/manual planet/scribble (for-label racket) (for-label (this-package-in main)) (for-label (this-package-in rsound-compat))) @title{@bold{Vorbisfile}: Read sound data from .OGG Vorbis files} @author{gcr} @defmodule/this-package[main]{ This library lets Racket decode Ogg Vorbis files using @tt{libvorbisfile}, Xiph's reference Ogg decoder library. You can play Vorbis files with OpenAL using the @tt{(planet gcr/openal)} package, or you can convert them to RSounds for the @tt{(planet clements/rsound)} package. You can also extract metadata (frequency, channels, comments). Ogg Vorbis is a popular sound file format, used especially in games where CPU time comes at a premium. } @table-of-contents[] @section[#:tag "openal-example"]{Example: Streaming Vorbis files through OpenAL} One way of playing Vorbis files is by using the @tt{(planet gcr/openal)} package. This is more efficient than loading megabytes of sound data into memory before playing it, but it's also a bit more complicated than the @seclink["rsound-example"]{RSound example}. First, one must dig out the spellbook and incant the OpenAL Summoning Mantra: @codeblock{ #lang racket (require (planet gcr/openal) (planet gcr/libvorbisfile)) ;; Initialize OpenAL (see the docs for (planet gcr/openal)) (define device (open-device #f)) (define context (create-context device)) (set-current-context context) } From here, it's not rocket science to open a vorbis file and query basic information about it. @codeblock{ (define filename "/home/gcr/Music/Lights/Siberia/11 Flux and Flow.ogg") (printf "Playing file ~a\n" filename) ;; Open the file! (define m (open-vorbis-file filename)) (printf "Rate: ~a Channels: ~a Length: ~a sec\n" (vorbis-frequency m) (vorbis-channels m) (vorbis-length-time m)) } There are many ways to read the decompressed sound data. By far, the easiest is to use the @racket[make-vorbis-input-port] function to create a port that decodes the sound data into binary @racket[bytes] on-demand. @codeblock{ ;; To read the PCM samples, we make a port that supplies us with ;; the binary decompressed data. (define vorbis-binary (make-vorbis-input-port m 0 2 1)) ;; OpenAL expects: ;; 0 (Little-endian) ;; 2 (Word size; 16 bits) ;; 1 (Signed) } Now that we have a port that gives us raw sample data, we can stream it straight to an OpenAL source. This avoids reading the entire file into memory -- each block of sound is decoded right as it's needed. @codeblock{ ;; Make our OpenAL source (define source (car (gen-sources 1))) ;; Start streaming (define stream-thread (stream-port-to-source vorbis-binary source AL_FORMAT_STEREO16 (vorbis-frequency m))) ;; Start playing (play-source source) ;; OpenAL's stream-port-to-source returns a thread, so wait until we're ;; finished playing (thread-wait stream-thread) } Once we're done, we should clean up our OpenAL mess: @codeblock{ (set-current-context #f) (destroy-context! context) (close-device! device) } You should probably close the vorbis file when you're finished. @codeblock{ (close-vorbis-file! m) } Well, to be honest, I haven't tested whether this is really necessary, but do be a good citizen, OK? Memory corruption city is never a nice place to be. @section[#:tag "rsound-example"]{Example: Playing vorbis files with the @tt{rsound} package} Vorbisfile can also work with John Clements' @tt{(planet clements/rsound)} library. Of course, @tt{rsound} is far more mature and easier to use than @tt{(planet gcr/openal)}. Unfortunately, with the current implementation, converting a vorbis sound to an @tt{rsound} is quite inefficient because the entire sound must be loaded into memory before playing it, which costs many needless megabytes of RAM and many needless seconds of CPU time. With rsound, no extra setup is necessary -- just load the libraries and you're good to go. @codeblock{ #lang racket/base (require (planet gcr/libvorbisfile) (planet gcr/libvorbisfile/rsound-compat) (planet clements/rsound)) } Open and load a sound file to play: @codeblock{ (define filename "/home/gcr/Music/Lights/Siberia/14 Day One.ogg") (displayln "Loading sound...") (define o (open-vorbis-file filename)) (define s (vorbis->rsound o)) } And finally, play that sound! @codeblock{ (displayln "Playing 30s of sound...") (play s) (sleep 30) } RSound plays in the background, so the @racket[sleep] call is necessary; else the program will quit right when it starts. @section{File management} @defproc[(open-vorbis-file [path string?]) (or/c #f any/c)]{ Opens the .Ogg Vorbis given by @racket[path] and returns a handle to that file if successful, or @racket[#f] on any error. } @defproc[(close-vorbis-file! [vorbisfile any/c]) any/c]{ Closes the given @racket[vorbisfile]. Note: Don't call any functions on that file after it's gone! } @subsection{Querying information} @deftogether[( @defproc[(vorbis-length-samples [vorbisfile any/c]) exact-integer?] @defproc[(vorbis-length-time [vorbisfile any/c]) real?] )]{ How long is @racket[vorbisfile]? Returns the length in either seconds or number of PCM samples. Feast on them all! Pour every golden sapmle and every delicious second of that @racket[vorbisfile] into your starving eardrums! } @deftogether[( @defproc[(vorbis-current-time [vorbisfile any/c]) real?] @defproc[(vorbis-current-samples [vorbisfile any/c]) exact-integer?] )]{ Returns your current position in the file, in seconds or samples. Note that the reported position could be a few fractions of a second behind because we aggressively buffer the sound data to minimize the number of FFI calls. Don't be sad if the result doesn't change after reading a few samples of data. } @defproc[(vorbis-frequency [vorbisfile any/c]) exact-integer?]{ Returns the sample rate of @racket[vorbisfile]. For example, 44100 is pretty common. } @defproc[(vorbis-channels [vorbisfile any/c]) exact-integer?]{ Returns 1 for a mono @racket[vorbisfile] and 2 for stereo. May sometimes return more than 2 for surround-sound vorbis files or less than 1 for deaf vorbis files. } @defproc[(vorbis-comments [vorbisfile any/c]) (listof string?)]{ Returns a list of comments stored inside @racket[vorbisfile]. There's a standard for their format; @hyperlink["http://en.wikipedia.org/wiki/Vorbis_comment"]{see here} for more details. For example, a random file from my sound collection has these comments: @codeblock{ (list "minor_version=0" "compatible_brands=M4A mp42isom" "creation_time=2034-07-04 05:42:34" "title=Siberia" "artist=Lights" "ALBUMARTIST=Lights" "album=Siberia" "genre=Pop" "TRACKNUMBER=1/16" "DISCNUMBER=1/1" "gapless_playback=0" "date=2011-10-04T07:00:00Z" "copyright=℗ 2011 Lights Music Inc. Distributed exclusively through Last Gang Records Inc" "media_type=1" "encoder=Lavf53.21.0")} } @defproc[(vorbis-vendor [vorbisfile any/c]) string?]{ Returns information about the Vorbis implementation that encoded the file. For example, the same sound file above has @racket["Vendor: Lavf53.21.0"] as the vendor. } @defproc[(vorbis-avg-bitrate [vorbisfile any/c]) exact-integer?]{ Returns the average bitrate of @racket[vorbisfile]. If you're dealing with a wretched unseekable file, this will return the nominal bitrate, or the average of the upper and lower bitrates. } @subsection{Seeking around} @deftogether[( @defproc[(vorbis-seek-time! [vorbisfile any/c] [sec real?]) boolean?] @defproc[(vorbis-seek-samples! [vorbisfile any/c] [samples exact-integer?]) boolean?] )]{ If you're lost, you can seek to a given time in seconds or samples. The next reads will continue from this point. These functions return @racket[#t] upon success. To avoid loud pops and clicks when seeking around, libvorbisfile uses a process called @hyperlink["http://xiph.org/vorbis/doc/vorbisfile/crosslap.html"]{crow-slapping} to smooth out the sound gap between where you left and your new position in the file. This makes the result sound much nicer, but do understand that the next two samples of data won't be an exact reproduction of the original file. Certain heretics enjoy assembling unseekable vorbis files. Such tainted, blasphemous creatures will cause this function to return @racket[#f]. Shun them to preserve your own sanity. Unless you're dealing with internet radio or something i guess. } @section{Decoding data} There are two ways of decoding data. The hard way involves reading raw sample data to an existing byte buffer; the easy way allows you to merely create an input port that returns the binary decoded sample data. @subsection{The easy way} @defproc[(make-vorbis-input-port [vorbisfile any/c] [big-endian? exact-integer] [wordsize exact-integer?] [signed? exact-integer?]) input-port?]{ Creates an input port that returns raw binary PCM data in the given format. This port may misbehave if you read less than a few @racket[wordsize]s at a time. Beware! Closing this input port will also close the vorbis file. The @racket[big-endian?] parameter should be 1 if you want your samples big-endian or 0 if you prefer little-endian. The @racket[wordsize] parameter specifies how many bytes each sample should be -- 1 for 8-bit samples, 2 for 16-bit. The @racket[signed?] parameter should be 1 if you wish for signed integers and 0 if your delicate heart yearns for unsigned integers. } @subsection{The hard way} Here's your one-way ticket to Memory Corruption City. Not for the faint of heart. I'm just kidding of course. These functions aren't that hard to work with, but I feel bad that you're reading all the way down here because I spent a lot of hard work on @racket[make-vorbis-input-port] so you should probably use that loving craftsmanship instead of these crappy bindings. Unless you're the kind of person who cares about multiple vorbis bitstreams inside a single file. Heretic. @defproc[(make-bitstream-ptr) any/c]{ This function has no use; HOWEVER, libvorbis demands that you sacrifice the result of this function to every call to @racket[vorbis-read-to-byte-buf!] and @racket[vorbis-read-bytes!]. Technically, Ogg Vorbis files allow certain unscrupled people to cram several streams together into one .ogg container, essentially concatenating several ogg files together. Unfortunately, our humble library makes no meaningful distinction between them, instead forcing innocent developers like you to needlessly keep track of it. As such, we don't automatically handle the case when the sample rate changes for no reason. Thankfully, there aren't many Vorbis files that have multiple bitstreams inside them, and obviously, if any such files did exist, they are obvious contraband and you should immediately dispose of them. Yes, it's a pain. No, I don't care. } @defproc[(vorbis-read-to-byte-buf! [vorbisfile any/c] [buf bytes?] [len exact-integer?] [big-endian? exact-integer?] [wordsize exact-integer?] [signed? exact-integer?] [bitstream any/c]) exact-integer?]{ Reads up to @racket[len] bytes of sample data, overwriting the existing @racket[buf] buffer and returns the number of bytes written or 0 if you're at the end of the file. Note that vorbisfile will decode at most one frame of audio data at a time; thus, the actual amount of bytes read will often be far lower than @racket[len]. Remember what I said earlier about Memory Corruption City? If @racket[buf] is smaller than @racket[len], you're just begging for an all-expense-paid season pass. Don't even try it. The @racket[big-endian?] parameter should be 1 if you want your samples big-endian or 0 if you prefer little-endian. The @racket[wordsize] parameter specifies how many bytes each sample should be -- 1 for 8-bit samples, 2 for 16-bit. The @racket[signed?] parameter should be 1 if wish for signed integers and 0 if you yearn for unsigned integers. Finally, @racket[bitstream] should be the result of the call to @racket[(make-bitstream-ptr)]. } @defproc[(vorbis-read-bytes! [vf any/c] [len exact-integer?] [bigendianp exact-integer?] [wordsize exact-integer?] [signedp exact-integer?] [bitstream any/c]) (or/c bytes? eof-object?)]{ Slightly less stupid than @racket[vorbis-read-to-byte-buf!], this function will actually bother to return the bytestring for you, or the @racket[eof] object if you're at the end of the file. The @racket[big-endian?] parameter should be 1 if you want your samples big-endian or 0 if you prefer little-endian. The @racket[wordsize] parameter specifies how many bytes each sample should be -- 1 for 8-bit samples, 2 for 16-bit. The @racket[signed?] parameter should be 1 if wish for signed integers and 0 if you yearn for unsigned integers. Finally, @racket[bitstream] should be the result of the call to @racket[(make-bitstream-ptr)]. } @section{Rsound integration} @defmodule/this-package[rsound-compat] Vorbisfile can also bake your fine music into RSounds for John Clements' @tt{(planet clements/rsound)} library. @defproc[(vorbis->rsound [vorbisfile any/c]) any/c]{ Converts @racket[vorbisfile] to an @tt{rsound}. Note that this reads the entire file into memory first. } @section{License} Keep in mind that @tt{libvorbisfile} itself is licensed under a BSD-style license; see @(hyperlink "http://svn.xiph.org/trunk/vorbis/README" "here") for details. I don't know what license RSound has. The code in this @tt{(planet gcr/libvorbisfile)} package and this documentation is under the zlib license, reproduced below. @verbatim{ Copyright (c) 2012 gcr This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. }