packages feed

wavy (empty) → 0.1.0.0

raw patch · 20 files changed

+2276/−0 lines, 20 filesdep +basedep +binarydep +bytestringsetup-changed

Dependencies added: base, binary, bytestring, containers, filepath, pretty-show, riff, split, vector, wavy

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2013, Robert Massaioli++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Robert Massaioli nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Sound/Wav.hs view
@@ -0,0 +1,98 @@+-- | Everything about WAVE files is contained here: reading, editing and writing the data+-- within is all possible in this module.+module Sound.Wav (+   -- * Reading Riff Files+   -- | These functions allow you to read WAVE data from bytestrings and the filesystem. +   decodeWave+   , decodeWaveFile+   , decodeWaveFileOrFail+   , withWaveFile++   -- * Writing Riff Files+   -- | You will need to write out your WAVE file data eventually and these functions will+   -- allow you to do that.+   , encodeWaveFile++   -- * WAVE Data+   -- | There is a nested structure all WaveFiles and these pieces of data attempt to+   -- encapsulate that structure and make it easy for you to access the internal state of+   -- your files.+   , WaveFile(..)+   , WaveFormat(..)+   , WaveFact(..)+   , WaveInfo(..)+   , waveInfoDefault+   , IntegralWaveData(..)+   , IntegralWaveChannel+   , FloatingWaveData(..)+   , FloatingWaveChannel+   , WaveParseError++   -- * Info Editing and Retrieval+   -- | These functions let you get the metadata section of your WAVE files; otherwise+   -- known as the INFO section of the RIFF file.+   , getInfoData+   , updateWaveInfo++   -- * Audio Formats+   -- | You can place many different types of audio data inside an audio file, all of+   -- which is encoded in a different way. An audio format represents a different encoding+   -- of the audio data inside the data section of the file.+   , prettyShowAudioFormat+   , AudioFormat(..)++   -- * Extras+   -- | These are the exported extras of the package that you may find useful to browse+   -- and employ.+   , ByteOffset++   ) where++import Sound.Wav.Assemble +import Sound.Wav.AudioFormats+import Sound.Wav.Data+import Sound.Wav.Info+import Sound.Wav.Parse++import Data.Binary+import Data.Binary.Get++import qualified Data.ByteString.Lazy as L++-- Reading Functions++-- | Decodes a lazy bytestring into a WaveFile.+decodeWave+   :: L.ByteString   -- ^ The bytestring to attempt to parse.+   -> WaveFile       -- ^ The returned WaveFile containing WAVE data.+decodeWave = decode++-- | Give this function the path to a WAVE file and it will parse it into our internal+-- | representation.+decodeWaveFile +   :: FilePath       -- ^ The location of the WAVE file.+   -> IO WaveFile    -- ^ The returned WaveFile containing WAVE data.+decodeWaveFile = decodeFile++-- | Give this function the path to a WAVE file and it will Either return an error or a+-- WaveFile containing WAVE data. This does the exact same thing as decodeWaveFile except+-- that, instead of failing on an error, it returns the error in an either.+decodeWaveFileOrFail +   :: FilePath                                  -- ^ The location of the file that contains WAVE data.+   -> IO (Either (ByteOffset, String) WaveFile) -- ^ The error (left) or successful result (right).+decodeWaveFileOrFail = decodeFileOrFail++-- Writing Functions++-- | Outputs a WAVE file representation to a file (that can then be understood by other+-- WAVE file reading programs). The output of this function should fully comply to the+-- WAVE specifications.+encodeWaveFile +   :: FilePath -- ^ The file to dump the WAVE data.+   -> WaveFile -- ^ The internal representation of a WAVE file.+   -> IO ()    -- ^ This is performed inside an IO action.+encodeWaveFile = encodeFile++instance Binary WaveFile where+   put = assembleWaveFile+   get = getWaveFile
+ Sound/Wav/Assemble.hs view
@@ -0,0 +1,55 @@+module Sound.Wav.Assemble +   ( assembleWaveFile+   , toRiffFile+   ) where++import Sound.Wav.Data+import Sound.Wav.WaveFormat (putWaveFormat)+import Sound.Wav.Constants+import Sound.Wav.Info (waveInfoToRiffChunks)++import Control.Applicative ((<$>))+import Data.Binary.Put+import Data.Maybe (catMaybes)++import Data.Riff++assembleWaveFile :: WaveFile -> Put+assembleWaveFile = putRiffFile . toRiffFile++-- TODO expose this file so that people can modify it before it is written to the+-- filesystem+toRiffFile :: WaveFile -> RiffFile+toRiffFile waveFile = RiffFile +   { riffFileType = RIFF+   , riffFileFormatType = waveHeader+   , riffFileChildren = children+   }+   where+      children = catMaybes+         [ Just formatChunk+         -- TODO the ordering of these chunks should be configurable+         , infoChunk+         , Just dataChunk+         -- TODO we discard all other chunks, we should include them in here+         ]++      formatChunk :: RiffChunk+      formatChunk = RiffChunkChild+         { riffChunkId = waveFormatHeader+         , riffData = runPut . putWaveFormat . waveFormat $ waveFile+         }++      infoChunk :: Maybe RiffChunk+      infoChunk = (wrapInfoList . waveInfoToRiffChunks) <$> waveInfo waveFile+         where+            wrapInfoList contents = RiffChunkParent+               { riffFormTypeInfo = waveInfoListType +               , riffChunkChildren = contents+               }++      dataChunk :: RiffChunk+      dataChunk = RiffChunkChild+         { riffChunkId = waveDataHeader+         , riffData = waveData waveFile+         }
+ Sound/Wav/AudioFormats.hs view
@@ -0,0 +1,800 @@+{-+ - All raw data in here is copied from: + - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/RIFF.html+ -}++-- | The Data section of WAVE files can contain many different types of encoded data. It+-- can contain everytihng from raw PCM data (which this library uses as it's base+-- encoding) to a development encoding. The encoding that was used is saved in the Format+-- Chunk of the WAVE file and this module is responsible for making that one piece of data+-- easier to understand and deal with. We provide a number of helper functions to make the+-- AudioFormat easier to understand.+module Sound.Wav.AudioFormats +   ( AudioFormatData+   , AudioFormat(..)+   , getAudioFormat+   , putAudioFormat+   , prettyShowAudioFormat+   )+ where++import Data.Word+import Data.Maybe (fromMaybe)+import Data.Tuple (swap)++import qualified Data.Map as M++-- | A representation of the AudioFormatData as seen in the Format Section in the header+-- of a WAVE file.+type AudioFormatData = Word16++-- | Extracts the actual AudioFormat from the raw AudioFormatData+getAudioFormat +   :: AudioFormatData -- ^ The raw audio format data, likely taken from the header of a WAVE file.+   -> AudioFormat     -- ^ The actual AudioFormat as understood by wavy.+getAudioFormat key = fromMaybe defaultValue possibleKey  +   where+      possibleKey = M.lookup key audioFormatMap+      defaultValue = UnknownFormat key++-- | A conversion function from a known AudioFormat to it's representation as+-- AudioFormatData. This function is useful if you are planning on writing out your WAVE+-- data and sharing it.+putAudioFormat +   :: AudioFormat     -- ^ The audio format you wish to convert.+   -> AudioFormatData -- ^ The raw representation of the Audio Format.+putAudioFormat (UnknownFormat v) = v+putAudioFormat key = fromMaybe defaultValue possibleKey+   where+      possibleKey = M.lookup key reverseAudioFormatMap+      -- Unknown Code+      defaultValue = 0xe708++reverseAudioFormatMap :: M.Map AudioFormat AudioFormatData+reverseAudioFormatMap = M.fromList $ fmap swap audioCodecs++audioFormatMap :: M.Map AudioFormatData AudioFormat+audioFormatMap = M.fromList audioCodecs++audioCodecs :: [(AudioFormatData, AudioFormat)]+audioCodecs = +   [ (0x1, MicrosoftPCM)+   , (0x2, MicrosoftADPCM)+   , (0x3, MicrosoftIEEEfloat)+   , (0x4, CompaqVSELP)+   , (0x5, IBMCVSD)+   , (0x6, MicrosoftaLaw)+   , (0x7, MicrosoftuLaw)+   , (0x8, MicrosoftDTS)+   , (0x9, DRM)+   , (0xa, WMA9Speech)+   , (0xb, MicrosoftWindowsMediaRTVoice)+   , (0x10, OKIADPCM)+   , (0x11, IntelIMADVIADPCM)+   , (0x12, VideologicMediaspaceADPCM)+   , (0x13, SierraADPCM)+   , (0x14, AntexG723ADPCM)+   , (0x15, DSPSolutionsDIGISTD)+   , (0x16, DSPSolutionsDIGIFIX)+   , (0x17, DialoicOKIADPCM)+   , (0x18, MediaVisionADPCM)+   , (0x19, HPCU)+   , (0x1a, HPDynamicVoice)+   , (0x20, YamahaADPCM)+   , (0x21, SONARCSpeechCompression)+   , (0x22, DSPGroupTrueSpeech)+   , (0x23, EchoSpeechCorp)+   , (0x24, VirtualMusicAudiofileAF36)+   , (0x25, AudioProcessingTech)+   , (0x26, VirtualMusicAudiofileAF10)+   , (0x27, AculabProsody1612)+   , (0x28, MergingTechLRC)+   , (0x30, DolbyAC2)+   , (0x31, MicrosoftGSM610)+   , (0x32, MSNAudio)+   , (0x33, AntexADPCME)+   , (0x34, ControlResourcesVQLPC)+   , (0x35, DSPSolutionsDIGIREAL)+   , (0x36, DSPSolutionsDIGIADPCM)+   , (0x37, ControlResourcesCR10)+   , (0x38, NaturalMicroSystemsVBXADPCM)+   , (0x39, CrystalSemiconductorIMAADPCM)+   , (0x3a, EchoSpeechECHOSC3)+   , (0x3b, RockwellADPCM)+   , (0x3c, RockwellDIGITALK)+   , (0x3d, XebecMultimedia)+   , (0x40, AntexG721ADPCM)+   , (0x41, AntexG728CELP)+   , (0x42, MicrosoftMSG723)+   , (0x43, IBMAVCADPCM)+   , (0x45, ITUTG726)+   , (0x50, MicrosoftMPEG)+   , (0x51, RT23orPAC)+   , (0x52, InSoftRT24)+   , (0x53, InSoftPAC)+   , (0x55, MP3)+   , (0x59, Cirrus)+   , (0x60, CirrusLogic)+   , (0x61, ESSTechPCM)+   , (0x62, VoxwareInc)+   , (0x63, CanopusATRAC)+   , (0x64, APICOMG726ADPCM)+   , (0x65, APICOMG722ADPCM)+   , (0x66, MicrosoftDSAT)+   , (0x67, MicorsoftDSATDISPLAY)+   , (0x69, VoxwareByteAligned)+   , (0x70, VoxwareAC8)+   , (0x71, VoxwareAC10)+   , (0x72, VoxwareAC16)+   , (0x73, VoxwareAC20)+   , (0x74, VoxwareMetaVoice)+   , (0x75, VoxwareMetaSound)+   , (0x76, VoxwareRT29HW)+   , (0x77, VoxwareVR12)+   , (0x78, VoxwareVR18)+   , (0x79, VoxwareTQ40)+   , (0x7a, VoxwareSC3)+   , (0x7b, VoxwareSC3)+   , (0x80, Soundsoft)+   , (0x81, VoxwareTQ60)+   , (0x82, MicrosoftMSRT24)+   , (0x83, ATandTG729A)+   , (0x84, MotionPixelsMVIMV12)+   , (0x85, DataFusionG726)+   , (0x86, DataFusionGSM610)+   , (0x88, IteratedSystemsAudio)+   , (0x89, Onlive)+   , (0x8a, MultitudeIncFTSX20)+   , (0x8b, InfocomITSASG721ADPCM)+   , (0x8c, ConvediaG729)+   , (0x8d, NotspecifiedcongruencyInc)+   , (0x91, SiemensSBC24)+   , (0x92, SonicFoundryDolbyAC3APDIF)+   , (0x93, MediaSonicG723)+   , (0x94, AculabProsody8kbps)+   , (0x97, ZyXELADPCM)+   , (0x98, PhilipsLPCBB)+   , (0x99, StuderProfessionalAudioPacked)+   , (0xa0, MaldenPhonyTalk)+   , (0xa1, RacalRecorderGSM)+   , (0xa2, RacalRecorderG720a)+   , (0xa3, RacalG7231)+   , (0xa4, RacalTetraACELP)+   , (0xb0, NECAACNECCorporation)+   , (0xff, AAC)+   , (0x100, RhetorexADPCM)+   , (0x101, IBMuLaw)+   , (0x102, IBMaLaw)+   , (0x103, IBMADPCM)+   , (0x111, VivoG723)+   , (0x112, VivoSiren)+   , (0x120, PhilipsSpeechProcessingCELP)+   , (0x121, PhilipsSpeechProcessingGRUNDIG)+   , (0x123, DigitalG723)+   , (0x125, SanyoLDADPCM)+   , (0x130, SiproLabACEPLNET)+   , (0x131, SiproLabACELP4800)+   , (0x132, SiproLabACELP8V3)+   , (0x133, SiproLabG729)+   , (0x134, SiproLabG729A)+   , (0x135, SiproLabKelvin)+   , (0x136, VoiceAgeAMR)+   , (0x140, DictaphoneG726ADPCM)+   , (0x150, QualcommPureVoice)+   , (0x151, QualcommHalfRate)+   , (0x155, RingZeroSystemsTUBGSM)+   , (0x160, MicrosoftAudio1)+   , (0x161, WindowsMediaAudioV2V7V8V9DivXaudioSpecWMAAlexAC3Audio)+   , (0x162, WindowsMediaAudioProfessionalV9)+   , (0x163, WindowsMediaAudioLosslessV9)+   , (0x164, WMAProoverSPDIF)+   , (0x170, UNISYSNAPADPCM)+   , (0x171, UNISYSNAPULAW)+   , (0x172, UNISYSNAPALAW)+   , (0x173, UNISYSNAP16K)+   , (0x174, MMSYCOMACMSYC008SyComTechnologies)+   , (0x175, MMSYCOMACMSYC701G726LSyComTechnologies)+   , (0x176, MMSYCOMACMSYC701CELP54SyComTechnologies)+   , (0x177, MMSYCOMACMSYC701CELP68SyComTechnologies)+   , (0x178, KnowledgeAdventureADPCM)+   , (0x180, FraunhoferIISMPEG2AAC)+   , (0x190, DigitalTheaterSystemsDTSDS)+   , (0x200, CreativeLabsADPCM)+   , (0x202, CreativeLabsFASTSPEECH8)+   , (0x203, CreativeLabsFASTSPEECH10)+   , (0x210, UHERADPCM)+   , (0x215, UleadDVACM)+   , (0x216, UleadDVACM)+   , (0x220, QuarterdeckCorp)+   , (0x230, ILinkVC)+   , (0x240, AurealSemiconductorRawSport)+   , (0x241, ESSTAC3)+   , (0x250, InteractiveProductsHSX)+   , (0x251, InteractiveProductsRPELP)+   , (0x260, ConsistentCS2)+   , (0x270, SonySCX)+   , (0x271, SonySCY)+   , (0x272, SonyATRAC3)+   , (0x273, SonySPC)+   , (0x280, TELUMTelumInc)+   , (0x281, TELUMIATelumInc)+   , (0x285, NorcomVoiceSystemsADPCM)+   , (0x300, FujitsuFMTOWNSSND)+   , (0x301, FujitsuSpecnotspecified)+   , (0x302, FujitsuSpecnotspecified)+   , (0x303, FujitsuSpecnotspecified)+   , (0x304, FujitsuSpecnotspecified)+   , (0x305, FujitsuSpecnotspecified)+   , (0x306, FujitsuSpecnotspecified)+   , (0x307, FujitsuSpecnotspecified)+   , (0x308, FujitsuSpecnotspecified)+   , (0x350, MicronasSemiconductorsIncDevelopment)+   , (0x351, MicronasSemiconductorsIncCELP833)+   , (0x400, BrooktreeDigital)+   , (0x401, IntelMusicCoderSpecIMC)+   , (0x402, LigosIndeoAudio)+   , (0x450, QDesignMusic)+   , (0x500, On2VP7On2Technologies)+   , (0x501, On2VP6On2Technologies)+   , (0x680, ATandTVMEVMPCM)+   , (0x681, ATandTTCP)+   , (0x700, YMPEGAlphaSpecdummyforMPEG2compressor)+   , (0x8ae, ClearJumpLiteWaveSpeclossless)+   , (0x1000, OlivettiGSM)+   , (0x1001, OlivettiADPCM)+   , (0x1002, OlivettiCELP)+   , (0x1003, OlivettiSBC)+   , (0x1004, OlivettiOPR)+   , (0x1100, LernoutandHauspie)+   , (0x1101, LernoutandHauspieCELPcodec)+   , (0x1102, LernoutandHauspieSBCcodec)+   , (0x1103, LernoutandHauspieSBCcodec)+   , (0x1104, LernoutandHauspieSBCcodec)+   , (0x1400, NorrisCommInc)+   , (0x1401, ISIAudio)+   , (0x1500, ATandTSoundspaceMusicCompression)+   , (0x181c, VoxWareRT24speechcodec)+   , (0x181e, LucentelemediaAX24000PMusiccodec)+   , (0x1971, SonicFoundryLOSSLESS)+   , (0x1979, InningsTelecomIncADPCM)+   , (0x1c07, LucentSX8300Pspeechcodec)+   , (0x1c0c, LucentSX5363SG723compliantcodec)+   , (0x1f03, CUseeMeDigiTalkSpecexRocwell)+   , (0x1fc4, NCTSoftALF2CDACM)+   , (0x2000, FASTMultimediaDVM)+   , (0x2001, DolbyDTSSpecDigitalTheaterSystem)+   , (0x2002, RealAudio12144)+   , (0x2003, RealAudio1Slash2288)+   , (0x2004, RealAudioG2Slash8CookSpeclowbitrate)+   , (0x2005, RealAudio3Slash4Slash5MusicSpecDNET)+   , (0x2006, RealAudio10AACSpecRAAC)+   , (0x2007, RealAudio10AACPlusSpecRACP)+   , (0x2500, Reservedrangeto0x2600Microsoft)+   , (0x3313, MakeAVISSpecffvfwfakeAVIsoundfromAviSynthscripts)+   , (0x4143, DivioMPEG4AACaudio)+   , (0x4201, Nokiaadaptivemultirate)+   , (0x4243, DivioG726DivioInc)+   , (0x434c, LEADSpeech)+   , (0x564c, LEADVorbis)+   , (0x5756, WavPackAudio)+   , (0x674f, OggVorbisSpecmode1)+   , (0x6750, OggVorbisSpecmode2)+   , (0x6751, OggVorbisSpecmode3)+   , (0x676f, OggVorbisSpecmode1Plus)+   , (0x6770, OggVorbisSpecmode2Plus)+   , (0x6771, OggVorbisSpecmode3Plus)+   , (0x7000, ThreeCOMNBX3ComCorporation)+   , (0x706d, FAADAAC)+   , (0x7a21, GSMAMRSpecCBRnoSID)+   , (0x7a22, GSMAMRSpecVBRincludingSID)+   , (0xa100, ComverseInfosysLtdG7231)+   , (0xa101, ComverseInfosysLtdAVQSBC)+   , (0xa102, ComverseInfosysLtdOLDSBC)+   , (0xa103, SymbolTechnologiesG729A)+   , (0xa104, VoiceAgeAMRWBVoiceAgeCorporation)+   , (0xa105, IngenientTechnologiesIncG726)+   , (0xa106, ISOSlashMPEG4advancedaudioCoding)+   , (0xa107, EncoreSoftwareLtdG726)+   , (0xa109, SpeexACMCodecxiphorg)+   , (0xdfac, DebugModeSonicFoundryVegasFrameServerACMCodec)+   , (0xe708, Unknown)+   , (0xf1ac, FreeLosslessAudioCodecFLAC)+   , (0xfffe, Extensible)+   , (0xffff, Development)+   ]++-- | This is a massive data structure that contains every single different audio format+-- that I could find. It allows us to represent AudioFormats in a very human readable and+-- easy to understand manner insize wavy. This data structure will be constantly growing+-- and changing. Do not be surprised if names change or elements are added. Because this+-- list is always growing we have an UnknownFormat element that can wrap and+-- AudioFormatData so that, in the event that we do not know about an audio format. You+-- can still deal with it naturally.+data AudioFormat+   = MicrosoftPCM+   | MicrosoftADPCM+   | MicrosoftIEEEfloat+   | CompaqVSELP+   | IBMCVSD+   | MicrosoftaLaw+   | MicrosoftuLaw+   | MicrosoftDTS+   | DRM+   | WMA9Speech+   | MicrosoftWindowsMediaRTVoice+   | OKIADPCM+   | IntelIMADVIADPCM+   | VideologicMediaspaceADPCM+   | SierraADPCM+   | AntexG723ADPCM+   | DSPSolutionsDIGISTD+   | DSPSolutionsDIGIFIX+   | DialoicOKIADPCM+   | MediaVisionADPCM+   | HPCU+   | HPDynamicVoice+   | YamahaADPCM+   | SONARCSpeechCompression+   | DSPGroupTrueSpeech+   | EchoSpeechCorp+   | VirtualMusicAudiofileAF36+   | AudioProcessingTech+   | VirtualMusicAudiofileAF10+   | AculabProsody1612+   | MergingTechLRC+   | DolbyAC2+   | MicrosoftGSM610+   | MSNAudio+   | AntexADPCME+   | ControlResourcesVQLPC+   | DSPSolutionsDIGIREAL+   | DSPSolutionsDIGIADPCM+   | ControlResourcesCR10+   | NaturalMicroSystemsVBXADPCM+   | CrystalSemiconductorIMAADPCM+   | EchoSpeechECHOSC3+   | RockwellADPCM+   | RockwellDIGITALK+   | XebecMultimedia+   | AntexG721ADPCM+   | AntexG728CELP+   | MicrosoftMSG723+   | IBMAVCADPCM+   | ITUTG726+   | MicrosoftMPEG+   | RT23orPAC+   | InSoftRT24+   | InSoftPAC+   | MP3+   | Cirrus+   | CirrusLogic+   | ESSTechPCM+   | VoxwareInc+   | CanopusATRAC+   | APICOMG726ADPCM+   | APICOMG722ADPCM+   | MicrosoftDSAT+   | MicorsoftDSATDISPLAY+   | VoxwareByteAligned+   | VoxwareAC8+   | VoxwareAC10+   | VoxwareAC16+   | VoxwareAC20+   | VoxwareMetaVoice+   | VoxwareMetaSound+   | VoxwareRT29HW+   | VoxwareVR12+   | VoxwareVR18+   | VoxwareTQ40+   | VoxwareSC3+   | Soundsoft+   | VoxwareTQ60+   | MicrosoftMSRT24+   | ATandTG729A+   | MotionPixelsMVIMV12+   | DataFusionG726+   | DataFusionGSM610+   | IteratedSystemsAudio+   | Onlive+   | MultitudeIncFTSX20+   | InfocomITSASG721ADPCM+   | ConvediaG729+   | NotspecifiedcongruencyInc+   | SiemensSBC24+   | SonicFoundryDolbyAC3APDIF+   | MediaSonicG723+   | AculabProsody8kbps+   | ZyXELADPCM+   | PhilipsLPCBB+   | StuderProfessionalAudioPacked+   | MaldenPhonyTalk+   | RacalRecorderGSM+   | RacalRecorderG720a+   | RacalG7231+   | RacalTetraACELP+   | NECAACNECCorporation+   | AAC+   | RhetorexADPCM+   | IBMuLaw+   | IBMaLaw+   | IBMADPCM+   | VivoG723+   | VivoSiren+   | PhilipsSpeechProcessingCELP+   | PhilipsSpeechProcessingGRUNDIG+   | DigitalG723+   | SanyoLDADPCM+   | SiproLabACEPLNET+   | SiproLabACELP4800+   | SiproLabACELP8V3+   | SiproLabG729+   | SiproLabG729A+   | SiproLabKelvin+   | VoiceAgeAMR+   | DictaphoneG726ADPCM+   | QualcommPureVoice+   | QualcommHalfRate+   | RingZeroSystemsTUBGSM+   | MicrosoftAudio1+   | WindowsMediaAudioV2V7V8V9DivXaudioSpecWMAAlexAC3Audio+   | WindowsMediaAudioProfessionalV9+   | WindowsMediaAudioLosslessV9+   | WMAProoverSPDIF+   | UNISYSNAPADPCM+   | UNISYSNAPULAW+   | UNISYSNAPALAW+   | UNISYSNAP16K+   | MMSYCOMACMSYC008SyComTechnologies+   | MMSYCOMACMSYC701G726LSyComTechnologies+   | MMSYCOMACMSYC701CELP54SyComTechnologies+   | MMSYCOMACMSYC701CELP68SyComTechnologies+   | KnowledgeAdventureADPCM+   | FraunhoferIISMPEG2AAC+   | DigitalTheaterSystemsDTSDS+   | CreativeLabsADPCM+   | CreativeLabsFASTSPEECH8+   | CreativeLabsFASTSPEECH10+   | UHERADPCM+   | UleadDVACM+   | QuarterdeckCorp+   | ILinkVC+   | AurealSemiconductorRawSport+   | ESSTAC3+   | InteractiveProductsHSX+   | InteractiveProductsRPELP+   | ConsistentCS2+   | SonySCX+   | SonySCY+   | SonyATRAC3+   | SonySPC+   | TELUMTelumInc+   | TELUMIATelumInc+   | NorcomVoiceSystemsADPCM+   | FujitsuFMTOWNSSND+   | FujitsuSpecnotspecified+   | MicronasSemiconductorsIncDevelopment+   | MicronasSemiconductorsIncCELP833+   | BrooktreeDigital+   | IntelMusicCoderSpecIMC+   | LigosIndeoAudio+   | QDesignMusic+   | On2VP7On2Technologies+   | On2VP6On2Technologies+   | ATandTVMEVMPCM+   | ATandTTCP+   | YMPEGAlphaSpecdummyforMPEG2compressor+   | ClearJumpLiteWaveSpeclossless+   | OlivettiGSM+   | OlivettiADPCM+   | OlivettiCELP+   | OlivettiSBC+   | OlivettiOPR+   | LernoutandHauspie+   | LernoutandHauspieCELPcodec+   | LernoutandHauspieSBCcodec+   | NorrisCommInc+   | ISIAudio+   | ATandTSoundspaceMusicCompression+   | VoxWareRT24speechcodec+   | LucentelemediaAX24000PMusiccodec+   | SonicFoundryLOSSLESS+   | InningsTelecomIncADPCM+   | LucentSX8300Pspeechcodec+   | LucentSX5363SG723compliantcodec+   | CUseeMeDigiTalkSpecexRocwell+   | NCTSoftALF2CDACM+   | FASTMultimediaDVM+   | DolbyDTSSpecDigitalTheaterSystem+   | RealAudio12144+   | RealAudio1Slash2288+   | RealAudioG2Slash8CookSpeclowbitrate+   | RealAudio3Slash4Slash5MusicSpecDNET+   | RealAudio10AACSpecRAAC+   | RealAudio10AACPlusSpecRACP+   | Reservedrangeto0x2600Microsoft+   | MakeAVISSpecffvfwfakeAVIsoundfromAviSynthscripts+   | DivioMPEG4AACaudio+   | Nokiaadaptivemultirate+   | DivioG726DivioInc+   | LEADSpeech+   | LEADVorbis+   | WavPackAudio+   | OggVorbisSpecmode1+   | OggVorbisSpecmode2+   | OggVorbisSpecmode3+   | OggVorbisSpecmode1Plus+   | OggVorbisSpecmode2Plus+   | OggVorbisSpecmode3Plus+   | ThreeCOMNBX3ComCorporation+   | FAADAAC+   | GSMAMRSpecCBRnoSID+   | GSMAMRSpecVBRincludingSID+   | ComverseInfosysLtdG7231+   | ComverseInfosysLtdAVQSBC+   | ComverseInfosysLtdOLDSBC+   | SymbolTechnologiesG729A+   | VoiceAgeAMRWBVoiceAgeCorporation+   | IngenientTechnologiesIncG726+   | ISOSlashMPEG4advancedaudioCoding+   | EncoreSoftwareLtdG726+   | SpeexACMCodecxiphorg+   | DebugModeSonicFoundryVegasFrameServerACMCodec+   | Unknown+   | FreeLosslessAudioCodecFLAC+   | Extensible+   | Development+   | UnknownFormat AudioFormatData+   deriving(Show, Eq, Ord)++-- | This function is reponsible for converting an AudioFormat into a representation that+-- is human readable and what people would expect. It is useful if you need to display+-- your AudioFormat to a human and you want to know, quickly, what to call it.+prettyShowAudioFormat +   :: AudioFormat -- ^ The audio format that you wish to display to a human.+   -> String      -- ^ The human readable description of this audio format.+prettyShowAudioFormat (UnknownFormat formatNumber) = +   "Unknown Audio Format (Code: " ++ show formatNumber ++ ")"+prettyShowAudioFormat xs = fromMaybe (noPretty xs) $ M.lookup xs audioFormatShow+   where+      noPretty :: AudioFormat -> String+      noPretty = ("Raw Format: " ++) . show++audioFormatShow :: M.Map AudioFormat String+audioFormatShow = M.fromList+   [ (MicrosoftPCM, "Microsoft PCM")+   , (MicrosoftADPCM, "Microsoft ADPCM")+   , (MicrosoftIEEEfloat, "Microsoft IEEE float")+   , (CompaqVSELP, "Compaq VSELP")+   , (IBMCVSD, "IBM CVSD")+   , (MicrosoftaLaw, "Microsoft a-Law")+   , (MicrosoftuLaw, "Microsoft u-Law")+   , (MicrosoftDTS, "Microsoft DTS")+   , (DRM, "DRM")+   , (WMA9Speech, "WMA 9 Speech")+   , (MicrosoftWindowsMediaRTVoice, "Microsoft Windows Media RT Voice")+   , (OKIADPCM, "OKI-ADPCM")+   , (IntelIMADVIADPCM, "Intel IMA/DVI-ADPCM")+   , (VideologicMediaspaceADPCM, "Videologic Mediaspace ADPCM")+   , (SierraADPCM, "Sierra ADPCM")+   , (AntexG723ADPCM, "Antex G.723 ADPCM")+   , (DSPSolutionsDIGISTD, "DSP Solutions DIGISTD")+   , (DSPSolutionsDIGIFIX, "DSP Solutions DIGIFIX")+   , (DialoicOKIADPCM, "Dialoic OKI ADPCM")+   , (MediaVisionADPCM, "Media Vision ADPCM")+   , (HPCU, "HP CU")+   , (HPDynamicVoice, "HP Dynamic Voice")+   , (YamahaADPCM, "Yamaha ADPCM")+   , (SONARCSpeechCompression, "SONARC Speech Compression")+   , (DSPGroupTrueSpeech, "DSP Group True Speech")+   , (EchoSpeechCorp, "Echo Speech Corp.")+   , (VirtualMusicAudiofileAF36, "Virtual Music Audiofile AF36")+   , (AudioProcessingTech, "Audio Processing Tech.")+   , (VirtualMusicAudiofileAF10, "Virtual Music Audiofile AF10")+   , (AculabProsody1612, "Aculab Prosody 1612")+   , (MergingTechLRC, "Merging Tech. LRC")+   , (DolbyAC2, "Dolby AC2")+   , (MicrosoftGSM610, "Microsoft GSM610")+   , (MSNAudio, "MSN Audio")+   , (AntexADPCME, "Antex ADPCME")+   , (ControlResourcesVQLPC, "Control Resources VQLPC")+   , (DSPSolutionsDIGIREAL, "DSP Solutions DIGIREAL")+   , (DSPSolutionsDIGIADPCM, "DSP Solutions DIGIADPCM")+   , (ControlResourcesCR10, "Control Resources CR10")+   , (NaturalMicroSystemsVBXADPCM, "Natural MicroSystems VBX ADPCM")+   , (CrystalSemiconductorIMAADPCM, "Crystal Semiconductor IMA ADPCM")+   , (EchoSpeechECHOSC3, "Echo Speech ECHOSC3")+   , (RockwellADPCM, "Rockwell ADPCM")+   , (RockwellDIGITALK, "Rockwell DIGITALK")+   , (XebecMultimedia, "Xebec Multimedia")+   , (AntexG721ADPCM, "Antex G.721 ADPCM")+   , (AntexG728CELP, "Antex G.728 CELP")+   , (MicrosoftMSG723, "Microsoft MSG723")+   , (IBMAVCADPCM, "IBM AVC ADPCM")+   , (ITUTG726, "ITU-T G.726")+   , (MicrosoftMPEG, "Microsoft MPEG")+   , (RT23orPAC, "RT23 or PAC")+   , (InSoftRT24, "InSoft RT24")+   , (InSoftPAC, "InSoft PAC")+   , (MP3, "MP3")+   , (Cirrus, "Cirrus")+   , (CirrusLogic, "Cirrus Logic")+   , (ESSTechPCM, "ESS Tech. PCM")+   , (VoxwareInc, "Voxware Inc.")+   , (CanopusATRAC, "Canopus ATRAC")+   , (APICOMG726ADPCM, "APICOM G.726 ADPCM")+   , (APICOMG722ADPCM, "APICOM G.722 ADPCM")+   , (MicrosoftDSAT, "Microsoft DSAT")+   , (MicorsoftDSATDISPLAY, "Micorsoft DSAT DISPLAY")+   , (VoxwareByteAligned, "Voxware Byte Aligned")+   , (VoxwareAC8, "Voxware AC8")+   , (VoxwareAC10, "Voxware AC10")+   , (VoxwareAC16, "Voxware AC16")+   , (VoxwareAC20, "Voxware AC20")+   , (VoxwareMetaVoice, "Voxware MetaVoice")+   , (VoxwareMetaSound, "Voxware MetaSound")+   , (VoxwareRT29HW, "Voxware RT29HW")+   , (VoxwareVR12, "Voxware VR12")+   , (VoxwareVR18, "Voxware VR18")+   , (VoxwareTQ40, "Voxware TQ40")+   , (VoxwareSC3, "Voxware SC3")+   , (VoxwareSC3, "Voxware SC3")+   , (Soundsoft, "Soundsoft")+   , (VoxwareTQ60, "Voxware TQ60")+   , (MicrosoftMSRT24, "Microsoft MSRT24")+   , (ATandTG729A, "AT&T G.729A")+   , (MotionPixelsMVIMV12, "Motion Pixels MVI MV12")+   , (DataFusionG726, "DataFusion G.726")+   , (DataFusionGSM610, "DataFusion GSM610")+   , (IteratedSystemsAudio, "Iterated Systems Audio")+   , (Onlive, "Onlive")+   , (MultitudeIncFTSX20, "Multitude, Inc. FT SX20")+   , (InfocomITSASG721ADPCM, "Infocom ITS A/S G.721 ADPCM")+   , (ConvediaG729, "Convedia G729")+   , (NotspecifiedcongruencyInc, "Not specified congruency, Inc.")+   , (SiemensSBC24, "Siemens SBC24")+   , (SonicFoundryDolbyAC3APDIF, "Sonic Foundry Dolby AC3 APDIF")+   , (MediaSonicG723, "MediaSonic G.723")+   , (AculabProsody8kbps, "Aculab Prosody 8kbps")+   , (ZyXELADPCM, "ZyXEL ADPCM")+   , (PhilipsLPCBB, "Philips LPCBB")+   , (StuderProfessionalAudioPacked, "Studer Professional Audio Packed")+   , (MaldenPhonyTalk, "Malden PhonyTalk")+   , (RacalRecorderGSM, "Racal Recorder GSM")+   , (RacalRecorderG720a, "Racal Recorder G720.a")+   , (RacalG7231, "Racal G723.1")+   , (RacalTetraACELP, "Racal Tetra ACELP")+   , (NECAACNECCorporation, "NEC AAC NEC Corporation")+   , (AAC, "AAC")+   , (RhetorexADPCM, "Rhetorex ADPCM")+   , (IBMuLaw, "IBM u-Law")+   , (IBMaLaw, "IBM a-Law")+   , (IBMADPCM, "IBM ADPCM")+   , (VivoG723, "Vivo G.723")+   , (VivoSiren, "Vivo Siren")+   , (PhilipsSpeechProcessingCELP, "Philips Speech Processing CELP")+   , (PhilipsSpeechProcessingGRUNDIG, "Philips Speech Processing GRUNDIG")+   , (DigitalG723, "Digital G.723")+   , (SanyoLDADPCM, "Sanyo LD ADPCM")+   , (SiproLabACEPLNET, "Sipro Lab ACEPLNET")+   , (SiproLabACELP4800, "Sipro Lab ACELP4800")+   , (SiproLabACELP8V3, "Sipro Lab ACELP8V3")+   , (SiproLabG729, "Sipro Lab G.729")+   , (SiproLabG729A, "Sipro Lab G.729A")+   , (SiproLabKelvin, "Sipro Lab Kelvin")+   , (VoiceAgeAMR, "VoiceAge AMR")+   , (DictaphoneG726ADPCM, "Dictaphone G.726 ADPCM")+   , (QualcommPureVoice, "Qualcomm PureVoice")+   , (QualcommHalfRate, "Qualcomm HalfRate")+   , (RingZeroSystemsTUBGSM, "Ring Zero Systems TUBGSM")+   , (MicrosoftAudio1, "Microsoft Audio1")+   , (WindowsMediaAudioV2V7V8V9DivXaudioSpecWMAAlexAC3Audio, "Windows Media Audio V2 V7 V8 V9 / DivX audio (WMA) / Alex AC3 Audio")+   , (WindowsMediaAudioProfessionalV9, "Windows Media Audio Professional V9")+   , (WindowsMediaAudioLosslessV9, "Windows Media Audio Lossless V9")+   , (WMAProoverSPDIF, "WMA Pro over S/PDIF")+   , (UNISYSNAPADPCM, "UNISYS NAP ADPCM")+   , (UNISYSNAPULAW, "UNISYS NAP ULAW")+   , (UNISYSNAPALAW, "UNISYS NAP ALAW")+   , (UNISYSNAP16K, "UNISYS NAP 16K")+   , (MMSYCOMACMSYC008SyComTechnologies, "MM SYCOM ACM SYC008 SyCom Technologies")+   , (MMSYCOMACMSYC701G726LSyComTechnologies, "MM SYCOM ACM SYC701 G726L SyCom Technologies")+   , (MMSYCOMACMSYC701CELP54SyComTechnologies, "MM SYCOM ACM SYC701 CELP54 SyCom Technologies")+   , (MMSYCOMACMSYC701CELP68SyComTechnologies, "MM SYCOM ACM SYC701 CELP68 SyCom Technologies")+   , (KnowledgeAdventureADPCM, "Knowledge Adventure ADPCM")+   , (FraunhoferIISMPEG2AAC, "Fraunhofer IIS MPEG2AAC")+   , (DigitalTheaterSystemsDTSDS, "Digital Theater Systems DTS DS")+   , (CreativeLabsADPCM, "Creative Labs ADPCM")+   , (CreativeLabsFASTSPEECH8, "Creative Labs FASTSPEECH8")+   , (CreativeLabsFASTSPEECH10, "Creative Labs FASTSPEECH10")+   , (UHERADPCM, "UHER ADPCM")+   , (UleadDVACM, "Ulead DV ACM")+   , (UleadDVACM, "Ulead DV ACM")+   , (QuarterdeckCorp, "Quarterdeck Corp.")+   , (ILinkVC, "I-Link VC")+   , (AurealSemiconductorRawSport, "Aureal Semiconductor Raw Sport")+   , (ESSTAC3, "ESST AC3")+   , (InteractiveProductsHSX, "Interactive Products HSX")+   , (InteractiveProductsRPELP, "Interactive Products RPELP")+   , (ConsistentCS2, "Consistent CS2")+   , (SonySCX, "Sony SCX")+   , (SonySCY, "Sony SCY")+   , (SonyATRAC3, "Sony ATRAC3")+   , (SonySPC, "Sony SPC")+   , (TELUMTelumInc, "TELUM Telum Inc.")+   , (TELUMIATelumInc, "TELUMIA Telum Inc.")+   , (NorcomVoiceSystemsADPCM, "Norcom Voice Systems ADPCM")+   , (FujitsuFMTOWNSSND, "Fujitsu FM TOWNS SND")+   , (FujitsuSpecnotspecified, "Fujitsu (not specified)")+   , (MicronasSemiconductorsIncDevelopment, "Micronas Semiconductors, Inc. Development")+   , (MicronasSemiconductorsIncCELP833, "Micronas Semiconductors, Inc. CELP833")+   , (BrooktreeDigital, "Brooktree Digital")+   , (IntelMusicCoderSpecIMC, "Intel Music Coder (IMC)")+   , (LigosIndeoAudio, "Ligos Indeo Audio")+   , (QDesignMusic, "QDesign Music")+   , (On2VP7On2Technologies, "On2 VP7 On2 Technologies")+   , (On2VP6On2Technologies, "On2 VP6 On2 Technologies")+   , (ATandTVMEVMPCM, "AT&T VME VMPCM")+   , (ATandTTCP, "AT&T TCP")+   , (YMPEGAlphaSpecdummyforMPEG2compressor, "YMPEG Alpha (dummy for MPEG-2 compressor)")+   , (ClearJumpLiteWaveSpeclossless, "ClearJump LiteWave (lossless)")+   , (OlivettiGSM, "Olivetti GSM")+   , (OlivettiADPCM, "Olivetti ADPCM")+   , (OlivettiCELP, "Olivetti CELP")+   , (OlivettiSBC, "Olivetti SBC")+   , (OlivettiOPR, "Olivetti OPR")+   , (LernoutandHauspie, "Lernout & Hauspie")+   , (LernoutandHauspieCELPcodec, "Lernout & Hauspie CELP codec")+   , (LernoutandHauspieSBCcodec, "Lernout & Hauspie SBC codec")+   , (LernoutandHauspieSBCcodec, "Lernout & Hauspie SBC codec")+   , (LernoutandHauspieSBCcodec, "Lernout & Hauspie SBC codec")+   , (NorrisCommInc, "Norris Comm. Inc.")+   , (ISIAudio, "ISIAudio")+   , (ATandTSoundspaceMusicCompression, "AT&T Soundspace Music Compression")+   , (VoxWareRT24speechcodec, "VoxWare RT24 speech codec")+   , (LucentelemediaAX24000PMusiccodec, "Lucent elemedia AX24000P Music codec")+   , (SonicFoundryLOSSLESS, "Sonic Foundry LOSSLESS")+   , (InningsTelecomIncADPCM, "Innings Telecom Inc. ADPCM")+   , (LucentSX8300Pspeechcodec, "Lucent SX8300P speech codec")+   , (LucentSX5363SG723compliantcodec, "Lucent SX5363S G.723 compliant codec")+   , (CUseeMeDigiTalkSpecexRocwell, "CUseeMe DigiTalk (ex-Rocwell)")+   , (NCTSoftALF2CDACM, "NCT Soft ALF2CD ACM")+   , (FASTMultimediaDVM, "FAST Multimedia DVM")+   , (DolbyDTSSpecDigitalTheaterSystem, "Dolby DTS (Digital Theater System)")+   , (RealAudio12144, "RealAudio 1 / 2 14.4")+   , (RealAudio1Slash2288, "RealAudio 1 / 2 28.8")+   , (RealAudioG2Slash8CookSpeclowbitrate, "RealAudio G2 / 8 Cook (low bitrate)")+   , (RealAudio3Slash4Slash5MusicSpecDNET, "RealAudio 3 / 4 / 5 Music (DNET)")+   , (RealAudio10AACSpecRAAC, "RealAudio 10 AAC (RAAC)")+   , (RealAudio10AACPlusSpecRACP, "RealAudio 10 AAC+ (RACP)")+   , (Reservedrangeto0x2600Microsoft, "Reserved range to 0x2600 Microsoft")+   , (MakeAVISSpecffvfwfakeAVIsoundfromAviSynthscripts, "makeAVIS (ffvfw fake AVI sound from AviSynth scripts)")+   , (DivioMPEG4AACaudio, "Divio MPEG-4 AAC audio")+   , (Nokiaadaptivemultirate, "Nokia adaptive multirate")+   , (DivioG726DivioInc, "Divio G726 Divio, Inc.")+   , (LEADSpeech, "LEAD Speech")+   , (LEADVorbis, "LEAD Vorbis")+   , (WavPackAudio, "WavPack Audio")+   , (OggVorbisSpecmode1, "Ogg Vorbis (mode 1)")+   , (OggVorbisSpecmode2, "Ogg Vorbis (mode 2)")+   , (OggVorbisSpecmode3, "Ogg Vorbis (mode 3)")+   , (OggVorbisSpecmode1Plus, "Ogg Vorbis (mode 1+)")+   , (OggVorbisSpecmode2Plus, "Ogg Vorbis (mode 2+)")+   , (OggVorbisSpecmode3Plus, "Ogg Vorbis (mode 3+)")+   , (ThreeCOMNBX3ComCorporation, "3COM NBX 3Com Corporation")+   , (FAADAAC, "FAAD AAC")+   , (GSMAMRSpecCBRnoSID, "GSM-AMR (CBR, no SID)")+   , (GSMAMRSpecVBRincludingSID, "GSM-AMR (VBR, including SID)")+   , (ComverseInfosysLtdG7231, "Comverse Infosys Ltd. G723 1")+   , (ComverseInfosysLtdAVQSBC, "Comverse Infosys Ltd. AVQSBC")+   , (ComverseInfosysLtdOLDSBC, "Comverse Infosys Ltd. OLDSBC")+   , (SymbolTechnologiesG729A, "Symbol Technologies G729A")+   , (VoiceAgeAMRWBVoiceAgeCorporation, "VoiceAge AMR WB VoiceAge Corporation")+   , (IngenientTechnologiesIncG726, "Ingenient Technologies Inc. G726")+   , (ISOSlashMPEG4advancedaudioCoding, "ISO/MPEG-4 advanced audio Coding")+   , (EncoreSoftwareLtdG726, "Encore Software Ltd G726")+   , (SpeexACMCodecxiphorg, "Speex ACM Codec xiph.org")+   , (DebugModeSonicFoundryVegasFrameServerACMCodec, "DebugMode SonicFoundry Vegas FrameServer ACM Codec")+   , (Unknown, "Unknown -")+   , (FreeLosslessAudioCodecFLAC, "Free Lossless Audio Codec FLAC")+   , (Extensible, "Extensible")+   , (Development, "Development")+   ]
+ Sound/Wav/Binary.hs view
@@ -0,0 +1,31 @@+-- | This module provides helper binary functions to get integer values from binary streams and put+-- them back again.+module Sound.Wav.Binary where++import Data.Binary.Get+import Data.Binary.Put+import Data.Int++getInt8 :: Get Int8+getInt8 = fmap fromIntegral getWord8++getInt16le :: Get Int16+getInt16le = fmap fromIntegral getWord16le++getInt32le :: Get Int32+getInt32le = fmap fromIntegral getWord32le++getInt64le :: Get Int64+getInt64le = fmap fromIntegral getWord64le++putInt8 :: Int8 -> Put+putInt8 = putWord8 . fromIntegral++putInt16le :: Int16 -> Put+putInt16le = putWord16le . fromIntegral++putInt32le :: Int32 -> Put+putInt32le = putWord32le . fromIntegral++putInt64le :: Int64 -> Put+putInt64le = putWord64le . fromIntegral
+ Sound/Wav/ChannelData.hs view
@@ -0,0 +1,171 @@+-- | This module allows us to deal with channel data inside of a riff file. You may wish to extract+-- audio data from WAVE files or put audio data into wave files and your data may be in integral or+-- floating formats. These methods make it trivial for you to put that data inside WaveFiles. This+-- module is designed to make the manipulation of the channel data inside a wave file easier.+module Sound.Wav.ChannelData +   ( getWaveData+   , extractIntegralWaveData+   , extractFloatingWaveData+   , encodeIntegralWaveData+   , encodeFloatingWaveData+   , putIntegralWaveData+   , putFloatingWaveData+   ) where++import Sound.Wav.Data+import Sound.Wav.Binary+import Sound.Wav.Scale++import Control.Monad (replicateM)+import Control.Applicative ((<$>))+import Data.Binary.Get+import Data.Binary.Put+import Data.Bits+import Data.List (transpose)+import Data.List.Split (chunksOf)+import Data.Word+import Data.Int+import qualified Data.Vector as V++emptyRawWaveData = FloatingWaveData []+emptyIntegralWaveData = IntegralWaveData []++-- | Given a WaveFile you will either get back an integral lossless representation of the audio data+-- or you will get back a parser error.+extractIntegralWaveData +   :: WaveFile                                  -- ^ The wave file that contains the data that you wish to extract.+   -> Either WaveParseError IntegralWaveData    -- ^ The final result, either an error or the data that you were looking for.+extractIntegralWaveData waveFile = case runGetOrFail getter rawData of+   Left (_, offset, error) -> Left $ error ++ " (" ++ show offset ++ ")"+   Right (_, _, parsedData) -> Right parsedData+   where+      rawData = waveData waveFile+      getter = getWaveDataIntegral (waveFormat waveFile)++-- | Extracts the data in the WaveFile in the floating format. This method allows you to view the+-- audio data in this file in the range [-1, 1] so that you can process a normalised view of the+-- data no matter how it was internally encoded. Be aware that the conversion to floating point and+-- back will be a lossy operation.+extractFloatingWaveData +   :: WaveFile                                  -- ^ The WaveFile that contains the data that you wish to extract.+   -> Either WaveParseError FloatingWaveData+extractFloatingWaveData waveFile = case runGetOrFail getter rawData of+   Left (_, offset, error) -> Left $ error ++ " (" ++ show offset ++ ")"+   Right (_, _, parsedData) -> Right parsedData+   where+      rawData = waveData waveFile+      getter = getWaveDataFloating $ waveFormat waveFile++-- TODO If the WaveFile has a fact chunk then we should update it at this point+-- | Given a WaveFile replace it's data contents with the integral data that you provide.+encodeIntegralWaveData +   :: WaveFile          -- ^ The WaveFile that you wish to fill with data.+   -> IntegralWaveData  -- ^ The integral data to be placed inside the WaveFile.+   -> WaveFile          -- ^ A WaveFile containing the data that you provided (formatted correctly).+encodeIntegralWaveData file rawData = file { waveData = runPut putter }+   where+      putter = putIntegralWaveData (waveFormat file) rawData++-- | Given a WaveFile replace it's data contents with the floating data that you provide.+encodeFloatingWaveData +   :: WaveFile          -- ^ The WaveFile that will be used to contain the provided data.+   -> FloatingWaveData  -- ^ The floating data to be placed inside the WaveFile. Will be converted to integrals internally and will therefore be a lossy process.+   -> WaveFile          -- ^ A WaveFile containing the data that you provided (formatted correctly).+encodeFloatingWaveData file rawData = file { waveData = runPut putter }+   where+      putter = putFloatingWaveData (waveFormat file) rawData++-- | A putter for integral wave data given a format so that you can output correctly formatted data+-- to any stream.+putIntegralWaveData +   :: WaveFormat        -- ^ The format that the wave data should be encoded into.+   -> IntegralWaveData  -- ^ The integral data that should be pushed out to the stream.+   -> Put               -- ^ A Put context that will be used to write the data out.+putIntegralWaveData format (IntegralWaveData rawData) = mapM_ putter $ interleaveData rawData+   where+      putter :: Int64 -> Put+      putter = wordPutter (bytesPerChannelSample format)++-- | A putter for floating wave data given a format so that you can output correctly formatted data+-- to any stream.+putFloatingWaveData +   :: WaveFormat        -- ^ The format that the WAVE data should be encoded into.+   -> FloatingWaveData  -- ^ The floating data that should be pushed out to the stream.+   -> Put               -- ^ A Put context that will be used to write the data out.+putFloatingWaveData format (FloatingWaveData rawData) = +   mapM_ (putter . floatToInt) $ interleaveData rawData+   where+      putter :: Int64 -> Put+      putter = wordPutter (bytesPerChannelSample format) ++bytesPerChannelSample :: WaveFormat -> Word16+bytesPerChannelSample format = waveBitsPerSample format `divRoundUp` 8++interleaveData :: [V.Vector a] -> [a]+interleaveData = concat . transpose . fmap V.toList++-- When getting or putting words scale to and from whatever format the data comes in and+-- get out.+wordPutter :: (Num a, Show a, Eq a) => a -> Int64 -> Put+wordPutter 1 = putInt8 . zeroStable (0 :: Int8)+wordPutter 2 = putInt16le . zeroStable (0 :: Int16)+wordPutter 3 = putInt32le . zeroStable (0 :: Int32)+wordPutter 4 = putInt64le+wordPutter x = \_ -> fail $ "The is no word putter for byte size " ++ show x++wordGetter :: (Num a, Show a, Eq a) => a -> Get Int64+wordGetter 1 = fmap zeroStable64 getInt8+wordGetter 2 = fmap zeroStable64 getInt16le+wordGetter 3 = fmap zeroStable64 getInt32le+wordGetter 4 = getInt64le+wordGetter x = fail $ "Could not get a valid word getter for bytes " ++ show x ++ "."++zeroStable64 :: (Bits a, Integral a) => a -> Int64+zeroStable64 = zeroStable (0 :: Int64)++-- | Getting the data values back in a precise manner from a WAVE file requires that you get them in+-- integral format. This allows you to get the data efficiently exactly as it is in the audio file.+-- This process is invertible without loss of data.+getWaveDataIntegral :: WaveFormat -> Get IntegralWaveData+getWaveDataIntegral format = fmap convertData (getWaveData format)+   where+      convertData :: [[Int64]] -> IntegralWaveData+      convertData = IntegralWaveData . fmap V.fromList++-- | Sometimes when you are dealing with audio data you want to get the numbers as floating values+-- in the range [-1, 1]. This method will allow you to parse the data out of a WaveFile straight+-- into a floating point format. Please be aware that this process is lossy.+getWaveDataFloating :: WaveFormat -> Get FloatingWaveData+getWaveDataFloating format = fmap convertData (getWaveData format)+   where+      convertData :: [[Int64]] -> FloatingWaveData+      convertData = FloatingWaveData . fmap (V.fromList . fmap intToFloat)++intToFloat :: Int64 -> Double+intToFloat x = fromIntegral x / fromIntegral (maxBound :: Int64)++floatToInt :: Double -> Int64+floatToInt x = round $ x * fromIntegral (maxBound :: Int64)++-- | Generate a getter that will parse a WAVE "data " chunk from a raw stream given a wave format.+getWaveData +   :: WaveFormat     -- ^ The format that the data is in.+   -> Get [[Int64]]  -- ^ The audio data, normalised into a stardard container.+getWaveData format = do+   dataLength <- remaining+   let readableWords = fromIntegral $ dataLength `div` bytesPerChannelSample+   (transpose . chunksOf channels) <$> getNWords readableWords+   where+      getNWords :: Int -> Get [Int64]+      getNWords words = replicateM words $ wordGetter bytesPerChannelSample++      channels :: Int+      channels = fromIntegral $ waveNumChannels format++      bytesPerChannelSample :: Int64+      bytesPerChannelSample = fromIntegral $ waveBitsPerSample format `divRoundUp` 8++divRoundUp ::  Integral a => a -> a -> a+divRoundUp num den = case num `divMod` den of+   (x, 0) -> x+   (x, _) -> x + 1
+ Sound/Wav/Constants.hs view
@@ -0,0 +1,7 @@+module Sound.Wav.Constants where++-- These are the Wave File RIFF Identifiers+waveHeader        = "WAVE"+waveFormatHeader  = "fmt "+waveDataHeader    = "data"+waveInfoListType  = "INFO"
+ Sound/Wav/Data.hs view
@@ -0,0 +1,141 @@+-- | This module contains almost all of the Data structures required to deal with WAVE files.+module Sound.Wav.Data where++import Data.Word+import Data.Int (Int64)+import Data.Vector++import qualified Data.ByteString.Lazy as BL++import Sound.Wav.AudioFormats++type ChunkSize = Word32       -- ^ Size of a chunk in an AudioFile. Has a maximum bound.+type SampleRate = Word32      -- ^ Sample Rate in an audio file.+type ByteRate = Word32        -- ^ Byte Rate in an audio file.+type BlockAlignment = Word16  -- ^ Represents the block alignment for the audio data.+type BitsPerSample = Word16   -- ^ Represents how many bits are required per sample.++-- | The representation for parser errors when attempting to read WaveFiles or their data.+type WaveParseError = String+-- | The format for raw, unparsed WaveData.+type RawWaveData = BL.ByteString++-- | The representation of a WaveFile. This ADT is this libraries representation of a WaveFile. It+-- is important to note that the format chunk and data chunks are not optional. Also, the wave data+-- is left in it's raw format so that it can be parsed appropriately later depending on wether you+-- even want to parse the data or if you have the data in a special encoding that requires special+-- handling.+data WaveFile = WaveFile+   { waveFormat :: WaveFormat    -- ^ The format chunk that specifies what data is present in this audio file.+   , waveData :: RawWaveData     -- ^ The unparsed wave data so that you can choose to parse it in whichever way you please, if at all. Having this option makes metadata queries on wave files extremely fast.+   , waveFact :: Maybe WaveFact  -- ^ A potential FACT chunk in the WaveFile.+   , waveInfo :: Maybe WaveInfo  -- ^ An optional INFO chunk in the wave file that contains many different forms of metadata.+   }+   deriving (Show)++-- | Each Riff file has a Format chunk and this data structure encapsulates the data that+-- is usually contained within. The format chunk gives you useful information: such as+-- what encoding was run over the data in the file and how many bits were used per sample.+data WaveFormat = WaveFormat+   { waveAudioFormat :: AudioFormat        -- ^ The audio format that this file was encoded with.+   , waveNumChannels :: Word16             -- ^ The number of channels in this recorded data. +                                       -- This is the difference between Mono, Stereo and more.+   , waveSampleRate :: SampleRate          -- ^ The rate at which samples were taken. Measured in Hz.+   , waveByteRate :: ByteRate              -- ^ The rate at which bytes should be consumed in Hz.+   , waveBlockAlignment :: BlockAlignment  -- ^ The number of bytes per block in this file.+   , waveBitsPerSample :: BitsPerSample    -- ^ The number of bits of data in every sample. This is +                                       -- important as it gives you an upper and lower bound +                                       -- on the values present in the data.+   }+   deriving (Show)++-- | From the specifications:+--+-- \"The fact chunk is required if the waveform data is+-- contained in a wavl LIST chunk and for all compressed +-- audio formats. The chunk is not required for PCM files +-- using the data chunk format.\"+--+-- This means that this section will become more important as this library matures and+-- begins to support a whole range of 'AudioFormat's.+data WaveFact = WaveFact+   { waveFactSampleCount :: Word32 -- ^ The number of WAVE samples in this file.+   }+   deriving (Show)++-- | This datatype defines an INFO chunk and our internal representation of it. It is+-- actually defined very clearly in section 2-14 of the Spec and we have tried to mirror+-- that representation here. The spec says the following:+--+-- > An INFO list should contain only the following+-- > chunks. New chunks may be defined, but an application+-- > should ignore any chunk it doesn't understand. The+-- > chunks listed below may only appear in an INFO list.+-- > Each chunk contains a ZSTR, or null-terminated text+-- > string.+-- +-- Manipulations of that data structure should adhere to that specification.+data WaveInfo = WaveInfo+   { archiveLocation       :: Maybe String+   , artist                :: Maybe String+   , commissionedBy        :: Maybe String+   , comments              :: Maybe String+   , copyrights            :: Maybe [String]+   , creationDate          :: Maybe String+   , croppedDetails        :: Maybe String+   , originalDimensions    :: Maybe String+   , dotsPerInch           :: Maybe String+   , engineers             :: Maybe [String]+   , genre                 :: Maybe String+   , keywords              :: Maybe [String]+   , lightness             :: Maybe String +   , originalMedium        :: Maybe String +   , name                  :: Maybe String+   , coloursInPalette      :: Maybe String+   , originalProduct       :: Maybe String+   , subject               :: Maybe String+   -- TODO make sure we output our name+   , creationSoftware      :: Maybe String +   , sharpness             :: Maybe String +   , contentSource         :: Maybe String+   , originalForm          :: Maybe String+   , technician            :: Maybe String +   }+   deriving (Show)+   +-- | This is the default value that an INFO chunk can take, a chunk that contains no+-- metadata at all.+waveInfoDefault = WaveInfo+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing+   Nothing++-- | A multi-channel structure for holding integral wave data efficiently.+data IntegralWaveData = IntegralWaveData [IntegralWaveChannel]+-- | A multi-channel structure for holding floating wave data efficiently.+data FloatingWaveData = FloatingWaveData [FloatingWaveChannel]++-- | An efficient data structure for holding a single channel of integral wave data.+type IntegralWaveChannel = Vector Int64+-- | An efficient data structure for holding a single channel of floating wave data.+type FloatingWaveChannel = Vector Double
+ Sound/Wav/Info.hs view
@@ -0,0 +1,118 @@+-- | The Sound.Wav.Info module is responsible for abolutely everything in the INFO+-- Metadata chunk of a RIFF file. It allows you to read, write and modify that chunk of+-- data easily.+module Sound.Wav.Info +   ( parseWaveInfo+   , updateWaveInfo+   , getInfoData+   , waveInfoToRiffChunks+   ) where++import Data.List (intercalate)+import Data.List.Split (splitOn)+import Data.Maybe (catMaybes, fromMaybe)++import qualified Data.Riff as R+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Lazy.Char8 as BLC++import Sound.Wav.Data++import Control.Applicative ((<$>))++-- TODO the code in this function could be greatly cleaned up via lenses+-- | Update the INFO metadata chunk inside an existing WaveFile. This will allow you to+-- edit the metadata inside a file.+updateWaveInfo +   :: (WaveInfo -> WaveInfo)   -- ^ The conversion function from original to new metadata.+   -> WaveFile                   -- ^ The input WaveFile that will be modified.+   -> WaveFile                   -- ^ A new WaveFile that contains the updated INFO section.+updateWaveInfo updater waveFile = waveFile { waveInfo = updater <$> waveInfo waveFile }++-- | You want to be able to get the info chunk from your WaveFiles, however, if the info+-- chunk does not exist then you will be provided with a default info chunk.+getInfoData +   :: WaveFile    -- ^ The file that you wish to extract INFO metadata from.+   -> WaveInfo   -- ^ The info metadata.+getInfoData = fromMaybe waveInfoDefault . waveInfo++waveInfoToRiffChunks :: WaveInfo -> [R.RiffChunk]+waveInfoToRiffChunks waveInfo = catMaybes $ fmap (\x -> x waveInfo)+   [ convertString      "IARL" . archiveLocation+   , convertString      "IART" . artist+   , convertString      "ICMS" . commissionedBy +   , convertString      "ICMT" . comments+   , convertStringList  "ICOP" . copyrights+   , convertString      "ICRD" . creationDate+   , convertString      "ICRP" . croppedDetails+   , convertString      "IDIM" . originalDimensions+   , convertString      "IDPI" . dotsPerInch+   , convertStringList  "IENG" . engineers+   , convertString      "IGNR" . genre+   , convertStringList  "IKEY" . keywords+   , convertString      "ILGT" . lightness+   , convertString      "IMED" . originalMedium+   , convertString      "INAM" . name+   , convertString      "IPLT" . coloursInPalette+   , convertString      "IPRD" . originalProduct+   , convertString      "ISBJ" . subject+   -- TODO put our own name in here, then make it optional+   , convertString      "ISFT" . creationSoftware+   , convertString      "ISHP" . sharpness+   , convertString      "ISCR" . contentSource+   , convertString      "ISRF" . originalForm+   , convertString      "ITCH" . technician+   ]+   where+      convertString :: String -> Maybe String -> Maybe R.RiffChunk+      convertString riffId = fmap (R.RiffChunkChild riffId . BLC.pack)++      convertStringList :: String -> Maybe [String] -> Maybe R.RiffChunk+      convertStringList riffId = convertString riffId . fmap (intercalate "; ")++-- | Get the INFO metadata from a Byte Stream.+parseWaveInfo :: [R.RiffChunk] -> WaveInfo+parseWaveInfo = foldr appendWaveInfo waveInfoDefault ++appendWaveInfo :: R.RiffChunk -> WaveInfo -> WaveInfo+appendWaveInfo (R.RiffChunkChild riffId rawData) initial =+   case riffId of+      "IARL" -> initial { archiveLocation = Just asString}+      "IART" -> initial { artist = Just asString}+      "ICMS" -> initial { commissionedBy = Just asString}+      "ICMT" -> initial { comments = Just asString}+      "ICOP" -> initial { copyrights = Just asStringList}+      "ICRD" -> initial { creationDate = Just asString}+      "ICRP" -> initial { croppedDetails = Just asString}+      "IDIM" -> initial { originalDimensions = Just asString}+      "IDPI" -> initial { dotsPerInch = Just asString}+      "IENG" -> initial { engineers = Just asStringList}+      "IGNR" -> initial { genre = Just asString}+      "IKEY" -> initial { keywords = Just asStringList}+      "ILGT" -> initial { lightness = Just asString}+      "IMED" -> initial { originalMedium = Just asString}+      "INAM" -> initial { name = Just asString}+      "IPLT" -> initial { coloursInPalette = Just asString}+      "IPRD" -> initial { originalProduct = Just asString}+      "ISBJ" -> initial { subject = Just asString}+      "ISFT" -> initial { creationSoftware = Just asString}+      "ISHP" -> initial { sharpness = Just asString}+      "ISCR" -> initial { contentSource = Just asString}+      "ISRF" -> initial { originalForm = Just asString}+      "ITCH" -> initial { technician = Just asString}+      -- Skipping and ignoring them kinda sucks, in the future make it so +      -- that you put them in a buffer somewhere+      _ -> initial+   where+      asString = parseInfoString rawData+      asStringList = parseInfoStrings rawData+appendWaveInfo _ initial = initial++parseInfoString :: BL.ByteString -> String+parseInfoString = dropTrailingNull . BLC.unpack++dropTrailingNull :: String -> String+dropTrailingNull = reverse . dropWhile (== '\0') . reverse++parseInfoStrings :: BL.ByteString -> [String]+parseInfoStrings = splitOn "; " . parseInfoString
+ Sound/Wav/Parse.hs view
@@ -0,0 +1,94 @@+-- | This module contains all of the Parsing operations for this library. In our nonmenclature+-- Assemble is the opposite of parse so you should look at that module if you wish to perform the+-- opposite operations.+module Sound.Wav.Parse +   ( withWaveFile+   , parseWaveStream+   , getWaveFile+   ) where++import qualified Data.Riff as R+import Sound.Wav.Data+import Sound.Wav.WaveFormat+import Sound.Wav.Info (parseWaveInfo)+import Sound.Wav.Constants++import Data.Binary.Get+import qualified Data.ByteString.Lazy as BL+import System.IO (withBinaryFile, IOMode(..))++-- | We would like to be able to pass in a path to a WaveFile and a handler so that we don't have to+-- deal with opening / closing a file and parsing the contents just to apply a handler to the data.+-- This is a convenience method that just lets you do something with a wave file.+withWaveFile +   :: FilePath                                     -- ^ The location of the WaveFile in the filesystem.+   -> (Either WaveParseError WaveFile -> IO ())    -- ^ A handler for the parse result. Note that the parse may fail and you should handle that.+   -> IO ()                                        -- ^ An IO context is required because you are reading from the filesystem.+withWaveFile filePath action = withBinaryFile filePath ReadMode $ \h -> do+   waveData <- fmap parseWaveStream (BL.hGetContents h)+   action waveData++parseWaveStream :: BL.ByteString -> Either WaveParseError WaveFile+parseWaveStream input = case runGetOrFail getWaveFile input of+   Left (_, offset, error) -> Left $ error ++ " (" ++ show offset ++ ")"+   Right (_, _, waveFile) -> Right waveFile++-- | Provide a context that will parse a wave file.+getWaveFile :: Get WaveFile+getWaveFile = fromRiffFile =<< R.getRiffFile+      +fromRiffFile :: R.RiffFile -> Get WaveFile+fromRiffFile (R.RiffFile _ formatType chunks) = +   if formatType == waveHeader+      then fromRiffChunks chunks+      else fail $ "This is not a WAVE file format type was: " ++ formatType++fromRiffChunks :: [R.RiffChunk] -> Get WaveFile+fromRiffChunks chunks = do+   format <- runGetWaveFormat =<< formatChunk+   rawData <- dataChunk+   -- TODO potentially get the other chunks that we know about+   -- TODO Store the remaining chunks in an overflow of the riff chunks+   return WaveFile +      { waveFormat = format +      , waveData = R.riffData rawData+      , waveFact = Nothing +      , waveInfo = infoChunk+      }+   where+      formatChunk = onlyOneChunk waveFormatHeader $ filter (riffIdIs waveFormatHeader) chunks+      dataChunk = onlyOneChunk waveDataHeader $ filter (riffIdIs waveDataHeader) chunks+      infoChunk = case filter (riffListIdIs waveInfoListType) chunks of+         [] -> Nothing+         -- TODO what if there is more than one INFO chunk? is that allowed? How would we+         -- merge it anyway?+         (R.RiffChunkParent _ children : _) -> Just . parseWaveInfo $ children+         _ -> Nothing++runGetWaveFormat :: R.RiffChunk -> Get WaveFormat+runGetWaveFormat riffChunk@(R.RiffChunkChild _ _) = +   case runGetOrFail getWaveFormat (R.riffData riffChunk) of+      Left (_, offset, error) -> fail $ "Error in format chunk: " ++ error ++ postfix offset+      Right (_, _, waveFormat) -> return waveFormat+   where +      postfix offset = " (" ++ show offset ++ ")"+runGetWaveFormat _ = fail "Format chunk is not allowed to be a nested chunk!"++-- TODO parse the fact chunk out of WAVE files+getFactChunkHelper :: Get WaveFact+getFactChunkHelper = fmap WaveFact getWord32le++onlyOneChunk :: String -> [R.RiffChunk] -> Get R.RiffChunk+onlyOneChunk chunkType []  = fail $ "There were no chunks of type: " ++ chunkType +onlyOneChunk _         [x] = return x+onlyOneChunk chunkType xs  = fail $ "There are " ++ stringLength ++ " chunks of type '" ++ chunkType ++ "' in the WAVE data."+   where+      stringLength = show . length $ xs++riffIdIs :: String -> R.RiffChunk -> Bool+riffIdIs comp riffChunk@(R.RiffChunkChild _ _) = comp == R.riffChunkId riffChunk+riffIdIs _    _ = False++riffListIdIs :: String -> R.RiffChunk -> Bool+riffListIdIs comp (R.RiffChunkParent formatType _) = comp == formatType+riffListIdIs _    _ = False
+ Sound/Wav/Scale.hs view
@@ -0,0 +1,62 @@+-- | A module for various scale functions that we may need for the transformation of data in a wave+-- file.+module Sound.Wav.Scale +   ( bounds+   , scale+   , zeroStable+   ) where++import Data.Ratio+import Data.Bits++-- Thu May  1 08:28:15 EST 2014+-- The scale function works correctly but it does not maintain the position of zero. I+-- would expect zero to stay constant no matter how you transformed it. I think we might+-- need a positive half of the transform and a negative half of the transform just to keep+-- everything balanced. I am not yet sure how I will make that work for unsigned types.+-- Unfortunately I am beginning to this that this is a correct implimentation of this+-- function and that the movement of zero is just conversion noise. People will have to+-- learn to deal with it.+-- +-- Fri May  2 08:03:34 EST 2014+-- I have realised that I want a scale function that works in the same way that an+-- equivalence relation works. I want the following three rules to hold true:+--+-- Where s_ab means: the scale function s that scales a to b+--+-- Rule 1: s_ab 0 = 0 (Reflexive)+-- Rule 2: s_ba . s_ab $ x = x (Symmetric)+-- Rule 3: s_bc . s_ab $ x = s_ac x (Transitive)+--+-- So I should be able to write test cases that ensure that this is the case.++-- | Given a bounded type it returns a lower and upper bound result.+bounds :: Bounded a => (a, a)+bounds = (minBound, maxBound)++-- | Given two bounds linearly scale something fro the first range into the second. This method will+-- ensure that the endpoints of the bounds meet in the scale.+scale :: (Bounded a, Bounded b, Integral a, Integral b) => (a, a) -> (b, b) -> a -> b+scale (aLow, aHigh) (bLow, bHigh) start = fromIntegral endOffset+   where+      fi :: Integral d => d -> Integer+      fi = fromIntegral+      +      startOffset = toRatio $ fi start - fi aLow+      endOffset = round (startOffset * rat) - fi bLow++      rat = bRange % aRange++      aRange = fi aHigh - fi aLow+      bRange = fi bHigh - fi bLow++toRatio :: Integral a => a -> Ratio a+toRatio = flip (%) 1++-- | This is a scale function in which, given an example integer fro ma range it will scale a number+-- a into that range. The most important part of this scale is that zeroStable x 0 = 0.+zeroStable :: (Bits a, Bits b, Integral a, Integral b) => b -> a -> b+zeroStable example x = fromIntegral $ shift (fromIntegral x :: Integer) (toBits - fromBits) +   where+      fromBits = bitSize x+      toBits = bitSize example
+ Sound/Wav/WaveFormat.hs view
@@ -0,0 +1,47 @@+-- | This module handles parsing the "fmt " header of a WaveFile.+module Sound.Wav.WaveFormat +   ( getWaveFormat+   , putWaveFormat+   ) where++import Sound.Wav.Data+import Sound.Wav.AudioFormats++import Data.Binary+import Data.Binary.Get+import Data.Binary.Put++instance Binary WaveFormat where+   get = getWaveFormat+   put = putWaveFormat++-- | Will successfully parse the format header of a WaveFile. They are very well specified leaving+-- little room for error.+getWaveFormat :: Get WaveFormat+getWaveFormat = do+   -- TODO use the correct endian based on the correct parsing context+   audioFormat <- fmap getAudioFormat getWord16le+   numChannels <- getWord16le+   sampleRate <- getWord32le+   byteRate <- getWord32le+   blockAlignment <- getWord16le+   bitsPerSample <- getWord16le+   return WaveFormat+      { waveAudioFormat = audioFormat+      , waveNumChannels = numChannels+      , waveSampleRate = sampleRate+      , waveByteRate = byteRate+      , waveBlockAlignment = blockAlignment+      , waveBitsPerSample = bitsPerSample+      }++-- | Will successfully place a WaveFile fmt chunk into a binary stream. Follows the WAVE+-- specifications.+putWaveFormat :: WaveFormat-> Put+putWaveFormat format = do+   putWord16le . putAudioFormat . waveAudioFormat $ format+   putWord16le . waveNumChannels $ format+   putWord32le . waveSampleRate $ format+   putWord32le . waveByteRate $ format+   putWord16le . waveBlockAlignment $ format+   putWord16le . waveBitsPerSample $ format
+ src/Generate.hs view
@@ -0,0 +1,110 @@+module Main where++import Sound.Wav+import Sound.Wav.ChannelData++import Data.List (find)+import System.Environment (getArgs)+import System.Console.GetOpt++import qualified Data.Vector as V+import qualified Data.ByteString.Lazy as BL++import SineGenerator++data Flag +   = Version +   | Help +   | Frequency Double+   | Duration Integer+   deriving(Eq, Show)++options :: [OptDescr Flag]+options = +   [ Option "h" ["help"] (NoArg Help) "show this help message"+   , Option "f" ["frequency"] (OptArg (Frequency . parseFrequency) "1000") "the frequency of the wave that will be generated"+   , Option "d" ["duration"] (OptArg (Duration . parseDuration) "5") "the duration the sine wave will play for"+   ]++parseFrequency :: Maybe String -> Double+parseFrequency Nothing      = 1000.0+parseFrequency (Just value) = read value++parseDuration :: Maybe String -> Integer+parseDuration Nothing      = 5+parseDuration (Just value) = read value++chosenSampleRate :: Integral a => a+chosenSampleRate = 8000++-- TODO I should provide a format generation function+format :: WaveFormat+format = WaveFormat+   { waveAudioFormat = MicrosoftPCM+   , waveNumChannels = 1+   , waveSampleRate = chosenSampleRate+   , waveByteRate = chosenSampleRate * 2+   , waveBlockAlignment = 2+   , waveBitsPerSample = 16+   }++-- TODO generating an empty wave file is easy but maybe we still require a template for it+waveFileTemplate :: WaveFile+waveFileTemplate = WaveFile+   { waveFormat = format+   , waveData = BL.empty+   , waveFact = Nothing+   , waveInfo = Just $ waveInfoDefault { creationSoftware = Just "wavy (Sine Generate)" }+   }++-- If people want to do this then they can. It is quite simple to do. But maybe it would+-- be a nice convinience method. Maybe I could put it in a module called convenience that+-- is not included by default.+toFloatingWaveData :: [Double] -> FloatingWaveData+toFloatingWaveData rawData = FloatingWaveData $ [V.fromList rawData]++finalWaveFile :: GenerateInfo -> WaveFile+finalWaveFile genInfo = encodeFloatingWaveData waveFileTemplate $ toFloatingWaveData (generateWave genInfo)++header = "Usage: wave-generate-sine <filename>"+usageMessage = usageInfo header options++main = do+   args <- getArgs+   case getOpt Permute options args of+      (flags, [filename], []) -> handleFlags flags filename+      (_, _, msgs@(x:_)) -> error $ concat msgs ++ usageMessage+      (_, [], _) -> do+         putStrLn "No output filename provided. Please provide one."+         putStrLn usageMessage+      (_, filenames@(x:_), _) -> do+         putStr "Too many filenames provided: "+         print filenames+         putStrLn "Just provide one output file name."+         putStrLn usageMessage++handleFlags :: [Flag] -> FilePath -> IO ()+handleFlags flags filename+   | Help `elem` flags = putStrLn usageMessage+   | otherwise = do+      putStr $ "Generating sine wave in file '" ++ filename ++ "'..."+      encodeWaveFile filename $ finalWaveFile genInfo+      putStrLn "[Done]"+      where+         genInfo = GenerateInfo+            { duration = selectedDuration+            , frequency = selectedFrequency+            , sampleRate = chosenSampleRate+            }++         selectedDuration = maybe 5 fromDuration $ find isDuration flags+         selectedFrequency = maybe 1000.0 fromFrequency $ find isFrequency flags++fromDuration (Duration x) = x+fromFrequency (Frequency x) = x++isDuration (Duration _) = True+isDuration _            = False++isFrequency (Frequency _) = True+isFrequency _             = False
+ src/Identity.hs view
@@ -0,0 +1,17 @@+module Main where++import Sound.Wav++import System.Environment++main = do+   args <- getArgs+   case args of+      [] -> putStrLn "You need to provide a file for this to work."+      (x:_) -> decodeWaveFileOrFail x >>= writeWaveFile++writeWaveFile :: Either a WaveFile -> IO ()+writeWaveFile (Left _) = print "Could not even parse the file let alone output it."+-- TODO Instead of always using output.wav let the user specify what they want the output+-- file to be.+writeWaveFile (Right file) = encodeWaveFile "output.wav" file
+ src/Info.hs view
@@ -0,0 +1,169 @@+module Main where++{-+You should be able to pass as many .wav files as you like to this program and it should be able+to parse them all and show their information on STDOUT. You should start by implementing a verbose+mode by default and then you can consider implementing a quiet mode.++You should have such data as:++   - Audio Format+   - Bit Rate+   - Calculated length of audio in seconds (or even hours, minutes, seconds)+   - Name of the file and name of the piece in the info section+   - Absolutely everything from the info section of the file+-}++import System.Environment (getArgs)+import Data.Either (partitionEithers)+import Data.List (intersperse, null)+import Control.Monad (when)++import qualified Data.ByteString.Lazy as BL++import Sound.Wav++main = do +   files <- getArgs+   if null files+      then putStrLn "No files were given, nothing was parsed."+      else mapM decodeWaveFileOrFail files >>= handleData files++handleData :: [FilePath] -> [Either (ByteOffset, String) WaveFile] -> IO ()+handleData files parseData = do+   mapM_ handleError $ zip files errors+   sequence_ . spreadNewlines . fmap displayWaveFile $ zip files results+   where+      (errors, results) = partitionEithers parseData+      spreadNewlines :: [IO ()] -> [IO ()]+      spreadNewlines = intersperse $ putStrLn ""++handleError :: (FilePath, (ByteOffset, String)) -> IO ()+handleError (filename, (offset, errorMessage)) = do+   putStrLn $ "Error parsing: " ++ filename+   putStrLn $ "  " ++ errorMessage++displayWaveFile :: (FilePath, WaveFile) -> IO ()+displayWaveFile (filename, file) = do+   displayName filename file+   putStr " - "+   displayTime . audioTime $ file+   putStrLn ""+   displayFormatSection . waveFormat $ file+   putStrLn ""+   displayInfoSection . waveInfo $ file++displayName :: String -> WaveFile -> IO ()+displayName filename file =+   case name $ getInfoData file of+      Nothing -> putStr $ "File: " ++ filename+      Just name -> putStr $ name ++ " (" ++ filename ++ ")"++-- TODO copied from elsewhere, common code+divRoundUp :: Integral a => a -> a -> a+divRoundUp a b = res + signum rem+   where+      (res, rem) = a `divMod` b++displayTime :: (Integer, Integer, Integer) -> IO ()+displayTime (hours, minutes, seconds) = do+   when isHours $ putStr (show hours ++ "h")+   when isMinutes $ putStr (show minutes ++ "m")+   when (isSeconds || not (isHours && isMinutes)) $ putStr (show seconds ++ "s")+   where+      isHours = hours /= 0+      isMinutes = minutes /= 0+      isSeconds = seconds /= 0++audioTime :: WaveFile -> (Integer, Integer, Integer)+audioTime file = (hours, minutes, seconds)+   where +      totalSeconds = countSamples file `divRoundUp` fromIntegral (waveSampleRate format)+      (totalMinutes, seconds) = totalSeconds `divMod` 60+      (hours, minutes) = totalMinutes `divMod` 60 ++      format = waveFormat file +      countSamples :: WaveFile -> Integer+      countSamples waveFile = +         (fromIntegral . BL.length . waveData $ waveFile) `div` factor+         where+            format = waveFormat waveFile++            numChannels :: Integer+            numChannels = fromIntegral . waveNumChannels $ format++            bytesPerSample :: Integer+            bytesPerSample = flip div 8 . fromIntegral . waveBitsPerSample $ format++            factor = numChannels * bytesPerSample++prefix = ("     " ++)++displayFormatSection :: WaveFormat -> IO ()+displayFormatSection format = do+   putStrLn "   Format"+   putStrLn $ prefix "Audio Format: \t" ++ (prettyShowAudioFormat . waveAudioFormat $ format)+   putStrLn $ prefix "Channels: \t\t" ++ (show . waveNumChannels $ format)+   putStrLn $ prefix "Sample Rate: \t" ++ (show . waveSampleRate $ format)+   putStrLn $ prefix "Bits Per Sample: \t" ++ (show . waveBitsPerSample $ format)+   putStrLn $ prefix "Byte Rate: \t" ++ (show . waveByteRate $ format)+   putStrLn $ prefix "Block Alignment: \t" ++ (show . waveBlockAlignment $ format)+++infoHeader = "   INFO Metadata"++displayInfoSection :: Maybe WaveInfo -> IO ()+displayInfoSection Nothing = putStrLn $ infoHeader ++ " (None Present in File)"+displayInfoSection (Just infoData) = do+   putStrLn infoHeader+   pp archiveLocation "Archive Location"+   pp artist "Artist"+   pp commissionedBy "Comissioned By"+   pp comments "Comments"+   ppList copyrights "Copyrights"+   pp creationDate "Creation Date"+   pp croppedDetails "Cropped Details"+   pp originalDimensions "Original Dimensions"+   ppShow dotsPerInch "Dots Per Inch"+   ppList engineers "Engineers"+   pp genre "Genre"+   ppList keywords "Keywords"+   pp lightness "Lightness"+   pp originalMedium "Original Medium"+   ppShow coloursInPalette "Colours in Palette"+   pp originalProduct "Original Product"+   pp subject "Subject"+   pp creationSoftware "Creation Software"+   pp sharpness "Sharpness"+   pp contentSource "Content Source"+   pp originalForm "Original Form"+   pp technician "Technician"+   where+      pp = prettyShowInfo infoData+      ppList = prettyShowListInfo infoData+      ppShow = prettyShowConvert infoData++prettyShowConvert :: (Show a) => WaveInfo -> (WaveInfo -> Maybe a) -> String -> IO ()+prettyShowConvert chunk convert prefixWords =+   displayIfPresent (convert chunk) $ \showData -> do+      putStr $ prefix prefixWords+      putStr ": "+      print showData++prettyShowInfo :: WaveInfo -> (WaveInfo -> Maybe String) -> String -> IO ()+prettyShowInfo chunk convert prefixWords =+   displayIfPresent (convert chunk) $ \showData -> do+      putStr $ prefix prefixWords+      putStr ": "+      putStrLn showData++prettyShowListInfo :: WaveInfo -> (WaveInfo -> Maybe [String]) -> String -> IO ()+prettyShowListInfo chunk convert prefixWords = +   displayIfPresent (convert chunk) $ \showData -> do+      putStr $ prefix prefixWords+      putStr ": "+      sequence_ $ fmap (\x -> putStrLn $ prefix " - " ++ x) showData++displayIfPresent :: (Show a) => Maybe a -> (a -> IO ()) -> IO ()+displayIfPresent Nothing _ = return ()+displayIfPresent (Just x) f = f x
+ src/SineGenerator.hs view
@@ -0,0 +1,35 @@+module SineGenerator+   ( GenerateInfo(..)+   , generateWave+   ) where++data GenerateInfo = GenerateInfo+   { duration :: Integer+   , frequency :: Double+   , sampleRate :: Integer+   }+   deriving(Eq, Show)++generateWave :: GenerateInfo -> [Double]+generateWave genInfo = take samplesRequired $ map valueGenerate [1.0..]+   where +      valueGenerate = generateValue genInfo++      samplesRequired :: Int+      samplesRequired = fromIntegral (sr * dur)++      sr = sampleRate genInfo+      dur = duration genInfo++generateValue :: GenerateInfo -> Double -> Double+generateValue genInfo count = sin (count * freq / samplesPerSecond * twoPI)+   where+      samplesPerSecond :: Double+      samplesPerSecond = fromIntegral sr++      sr = sampleRate genInfo+      freq = frequency genInfo++twoPI :: Floating a => a+twoPI = pi * 2+
+ src/Splitter.hs view
@@ -0,0 +1,114 @@+module Main where+{- + - The purpose of this program is to provide a nice and easy way to split up multiple+ - segments of one audio file, separated by 'quiet' times, into their own separate files.+ -}++-- Currently I am thinking that downsampling, averaging and elevating again might be the+-- correct solution here.++-- What we want is a list of booleans to tell us which samples to split. We should ignore+-- massive runs of false in the array too and runs of true should end up in split files.++-- We have multiple channels, we should merge them all into the same channel by averaging+-- and then perform our logic.++import Control.Applicative ((<$>))+import Control.Monad (zipWithM_)+import Data.List (transpose, groupBy, foldr)+import Data.Maybe (fromMaybe)+import Data.Int+import qualified Data.Vector as V+import qualified Data.List.Split as S+import System.Environment (getArgs)+import System.Exit (exitWith, ExitCode(..))+import System.FilePath (splitExtension)++import Sound.Wav+import Sound.Wav.ChannelData++import VectorUtils++main = do+   args <- getArgs+   case args of+      [] -> do+         putStrLn "Need to provide a file to be split. Exiting."+         exitWith (ExitFailure 1)+      (x:_) -> splitFile x++-- TODO the best wayy to spot the spoken parts of the signal are to use the FFT output++splitFile :: FilePath -> IO ()+splitFile filePath = do+   riffFile <- decodeWaveFile filePath+   case splitWavFile riffFile of+      Left error -> putStrLn error+      Right files -> zipWithM_ encodeWaveFile filenames files+   where+      filenames = fmap filenameFor [1..]+      filenameFor n = base ++ "." ++ show n ++ ext+      (base, ext) = splitExtension filePath++-- TODO If a fact chunk is present then this function should update it+splitWavFile :: WaveFile -> Either WaveParseError [WaveFile]+splitWavFile originalFile = do+   extractedData <- extractFloatingWaveData originalFile+   return . fmap (encodeFloatingWaveData originalFile) $ splitChannels extractedData ++retentionWidth :: Int+retentionWidth = 10+lowerBoundPercent = 0.05++-- Splits one set of channels into equal channel splits+splitChannels :: FloatingWaveData -> [FloatingWaveData]+splitChannels (FloatingWaveData channels) = +   FloatingWaveData <$> [fmap zeroBadElements joinedChannels]+   where+      retain :: Int -> V.Vector Bool+      retain x = expand x . valuableSections . squishChannel x . fmap abs $ head channels++      joinedElements :: [V.Vector a] -> [V.Vector (Bool, a)]+      joinedElements = fmap (V.zip retention)+         where+            retention = retain retentionWidth+   +      zeroBadElements :: Num a => V.Vector (Bool, a) -> V.Vector a+      zeroBadElements = fmap (\(keep, val) -> if keep then val else 0) ++      joinedChannels = joinedElements channels++trueIsElem :: [(Bool, a)] -> Bool +trueIsElem a = True `elem` fmap fst a++fstEqual :: Eq a => (a, b) -> (a, c) -> Bool+fstEqual a b = fst a == fst b++-- TODO doing this function as a vector was previously slow. Try and come up with a more+-- efficient way to write this method that does not require converting back and forth+-- between lists+expand :: Int -> V.Vector a -> V.Vector a+expand count = asList (expandList count)++expandList :: Int -> [a] -> [a]+expandList count = foldr ((++) . replicate count) []++-- | The purpose of this function is to break up the file into sections that look valuable+-- and then we can begin to only take the sections that look good. +valuableSections :: FloatingWaveChannel -> V.Vector Bool+valuableSections absSamples = fmap (> lowerBound) absSamples+   where +      (minSample, maxSample) = minMax absSamples+      diff = maxSample - minSample+      lowerBound = minSample + lowerBoundPercent * diff++squishChannel :: Int -> FloatingWaveChannel -> FloatingWaveChannel+squishChannel factor samples = fmap floatingAverage . joinVectors $ groupedSamples+   where+      groupedSamples = vectorChunksOf factor samples++floatingAverage :: Floating a => [a] -> a+floatingAverage xs = sum xs / fromIntegral (length xs)++average :: Integral a => [a] -> a+average xs = fromIntegral $ sum xs `div` fromIntegral (length xs)
+ src/VectorUtils.hs view
@@ -0,0 +1,51 @@+module VectorUtils where++import qualified Data.Vector as V++-- An implimentation of the chunksOf function from the split package for Vectors. This+-- should be moved into the split or maybe a package called split-vector.+vectorChunksOf :: Int -> V.Vector a -> [V.Vector a]+vectorChunksOf chunkSize = go+   where+      go :: V.Vector a -> [V.Vector a]+      go v = +         if V.length v < chunkSize+            then +               let (x, xs) = V.splitAt chunkSize v+               in x : go xs+            else [v]++-- TODO how efficient is this operation? Could it be turned into something more efficient?+joinVectors :: [V.Vector a] -> V.Vector [a]+joinVectors = sequence++-- A convenience method to transform a vector into a list, perform an operation and+-- transform back again+asList :: ([a] -> [a]) -> V.Vector a -> V.Vector a+asList f vec = V.fromList $ f (V.toList vec)++-- An efficient version of the groupBy function for Vectors+-- Attempting to get it merged back in. See: https://github.com/haskell/vector/pull/24+groupByVector :: (a -> a -> Bool) -> V.Vector a -> [V.Vector a]+groupByVector eq vec = if V.null vec+   then []+   else V.take (1 + V.length ys) vec : groupByVector eq zs +   where+      (ys, zs) = V.span (eq x) xs+      x = V.head vec+      xs = V.tail vec++-- TODO this is a dodgy hack...really should remove it+instance Bounded Double where+   maxBound = 1 / 0+   minBound = -1 / 0++-- I implimented this method myself+minMax :: (Bounded a, Ord a) => V.Vector a -> (a, a)+minMax vec = if V.null vec+   then (maxBound, minBound)+   else (min x minVal, max x maxVal)+   where+      (minVal, maxVal) = minMax xs+      x = V.head vec+      xs = V.tail vec
+ wavy.cabal view
@@ -0,0 +1,124 @@+-- Initial wavy.cabal generated by cabal init.  For further documentation, +-- see http://haskell.org/cabal/users-guide/++-- The name of the package.+name:                wavy++-- The package version.  See the Haskell package versioning policy (PVP) +-- for standards guiding when and how versions should be incremented.+-- http://www.haskell.org/haskellwiki/Package_versioning_policy+-- PVP summary:      +-+------- breaking API changes+--                   | | +----- non-breaking API additions+--                   | | | +--- code changes with no API change+version:             0.1.0.0++-- A short (one-line) description of the package.+synopsis:            Process WAVE files in Haskell.++-- A longer description of the package.+description:         Wavy was designed to be a fast and efficient method of extracting and writing PCM+                     data to and from WAV files. It is here to help you make fast use of Audio+                     data in your Haskell programs and thus encourage many more audio projects in Haskell.++-- The license under which the package is released.+license:             BSD3++-- The file containing the license text.+license-file:        LICENSE++-- The package author(s).+author:              Robert Massaioli <robertmassaioli@gmail.com>++-- An email address to which users can send suggestions, bug reports, and +-- patches.+maintainer:          Robert Massaioli <robertmassaioli@gmail.com>++-- A copyright notice.+copyright:           (c) 2013-2014 Robert Massaioli <robertmassaioli@gmail.com>++category:            Sound++build-type:          Simple++-- Constraint on the version of Cabal needed to build this package.+cabal-version:       >=1.8++-- Bitbucket Information+homepage:            http://bitbucket.org/robertmassaioli/wavy+bug-reports:         http://bitbucket.org/robertmassaioli/wavy/issues+++library+  -- Modules exported by the library.+  exposed-modules:     Sound.Wav+                       , Sound.Wav.ChannelData++  ghc-options: -W+  +  -- Modules included in this library but not exported.+  other-modules:       Sound.Wav.Data+                       , Sound.Wav.Assemble+                       , Sound.Wav.Parse+                       , Sound.Wav.Info+                       , Sound.Wav.AudioFormats+                       , Sound.Wav.WaveFormat+                       , Sound.Wav.Constants+                       , Sound.Wav.Binary+                       , Sound.Wav.Scale+  +  -- Other library packages from which modules are imported.+  build-depends:       base >=4.5 && <5.0+                       , riff >=0.3 && <0.4+                       , binary ==0.7.*+                       , bytestring >=0.9 && <1.0+                       , split ==0.2.*+                       , containers ==0.4.*+                       , vector ==0.10.*++executable wave-identity+   hs-source-dirs: src+   main-is: Identity.hs++   -- ghc-options: -prof+  +  build-depends:       base >=4.5 && <5.0+                       , split+                       , pretty-show+                       , wavy++executable wave-info+   hs-source-dirs: src+   main-is: Info.hs++   -- ghc-options: -prof++  build-depends:        base >=4.5 && <5.0+                        , bytestring >=0.9 && <1.0+                        , wavy++executable wave-split+   hs-source-dirs: src+   main-is: Splitter.hs++   other-modules: VectorUtils++   ghc-options: -rtsopts++   build-depends:       base >=4.5 && <5.0+                        , wavy+                        , split+                        , vector ==0.10.*+                        , filepath ==1.3.*++executable wave-generate-sine+   hs-source-dirs: src+   main-is: Generate.hs++   other-modules:       SineGenerator++   ghc-options: -rtsopts++   build-depends:       base >=4.5 && <5.0+                        , wavy+                        , vector ==0.10.*+                        , bytestring >=0.9 && <1.0