proteaaudio 0.6.5 → 0.7.0
raw patch · 9 files changed
+201/−42 lines, 9 filesdep ~filepathsetup-changedPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: filepath
API changes (from Hackage documentation)
- Sound.ProteaAudio: sampleFromMemoryOgg :: (Ptr CChar) -> (Int) -> (Float) -> IO ((Sample))
+ Sound.ProteaAudio: sampleFromMemoryOgg :: ByteString -> Float -> IO Sample
- Sound.ProteaAudio: sampleFromMemoryWav :: (Ptr CChar) -> (Int) -> (Float) -> IO ((Sample))
+ Sound.ProteaAudio: sampleFromMemoryWav :: ByteString -> Float -> IO Sample
Files
- CHANGELOG.md +4/−0
- README.md +51/−0
- Setup.hs +0/−2
- Sound/ProteaAudio.chs +99/−9
- cbits/proAudioRt.cpp +6/−5
- cbits/proteaaudio_binding.cpp +2/−2
- cbits/proteaaudio_binding.h +2/−2
- example/play.hs +26/−14
- proteaaudio.cabal +11/−8
+ CHANGELOG.md view
@@ -0,0 +1,4 @@+# 0.7.0+- use ByteString for in-memory sample loading+- fix: c++ mixer init caused segfaults sometimes+- use PulseAudio backend on Linux
+ README.md view
@@ -0,0 +1,51 @@+# Overview++ProteaAudio is a stereo audio mixer/playback library+for Linux (native ALSA, Jack, and OSS),+Macintosh OS X (CoreAudio and Jack),+and Windows (DirectSound and ASIO) operating systems.+++# Example++```haskell+import Control.Monad+import System.Environment+import System.FilePath+import qualified Data.ByteString as SB+import Control.Concurrent++import Sound.ProteaAudio++waitPayback = do+ n <- soundActive+ when (n > 0) $ do+ threadDelay 1000000+ waitPayback++main = do+ args <- getArgs+ filename <- case args of+ a : _ -> pure a+ _ -> fail "usage: proteaaudio-play SAMPLE_FILE_NAME"++ result <- initAudio 64 44100 1024 -- max channels, mixing frequency, mixing buffer size+ unless result $ fail "failed to initialize the audio system"++ -- (A) load sample from file+ sampleA <- sampleFromFile filename 1.0 -- volume++ soundPlay sampleA 1 1 0 1 -- left volume, right volume, time difference between left and right, pitch factor for playback+ waitPayback++ -- (B) load from memory buffer+ buffer <- SB.readFile filename+ sampleB <- case takeExtension filename of+ ".ogg" -> sampleFromMemoryOgg buffer 1.0+ ".wav" -> sampleFromMemoryWav buffer 1.0++ soundPlay sampleB 1 1 0 1 -- left volume, right volume, time difference between left and right, pitch factor for playback+ waitPayback++ finishAudio+```
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
Sound/ProteaAudio.chs view
@@ -1,3 +1,9 @@+{-|+ ProteaAudio is a stereo audio mixer/playback library+ for Linux (native ALSA, Jack, and OSS),+ Macintosh OS X (CoreAudio and Jack),+ and Windows (DirectSound and ASIO) operating systems.+-} {-#LANGUAGE ForeignFunctionInterface#-} #include "proteaaudio_binding.h" module Sound.ProteaAudio (@@ -19,24 +25,108 @@ import Foreign import Foreign.C+import Data.ByteString (ByteString, useAsCStringLen) +-- | audio sample handle newtype Sample = Sample {#type sample_t#} toSample s = Sample s fromSample (Sample s) = s -{#fun initAudio {`Int', `Int', `Int'} -> `Bool'#}+-- | initializes the audio system+{#fun initAudio+ { `Int' -- ^ the maximum number of sounds that are played parallely. Computation time is linearly correlated to this factor.+ , `Int' -- ^ sample frequency of the playback in Hz. 22050 corresponds to FM radio 44100 is CD quality. Computation time is linearly correlated to this factor.+ , `Int' -- ^ the number of bytes that are sent to the sound card at once. Low numbers lead to smaller latencies but need more computation time (thread switches). If a too small number is chosen, the sounds might not be played continuously. The default value 512 guarantees a good latency below 40 ms at 22050 Hz sample frequency.+ } -> `Bool' -- ^ returns True on success+#}++-- | releases the audio device and cleans up resources {#fun finishAudio {} -> `()'#}-{#fun loaderAvailable {`String'} -> `Bool'#}-{#fun sampleFromMemoryWav {id `Ptr CChar', `Int', `Float'} -> `Sample' toSample#}-{#fun sampleFromMemoryOgg {id `Ptr CChar', `Int', `Float'} -> `Sample' toSample#}-{#fun sampleFromFile {`String', `Float'} -> `Sample' toSample#} -{#fun volume {`Float', `Float'} -> `()'#}+-- | checks if loader for this file type is available+{#fun loaderAvailable+ { `String' -- ^ file extension (e.g. ogg)+ } -> `Bool'+#}++-- | loads wav sound sample from memory buffer+{#fun _sampleFromMemoryWav+ { id `Ptr CChar' -- ^ memory buffer pointer+ , `Int' -- ^ memory buffer size in bytes+ , `Float' -- ^ volume+ } -> `Sample' toSample -- ^ returns handle+#}++-- | loads ogg sound sample from memory buffer+{#fun _sampleFromMemoryOgg+ { id `Ptr CChar' -- ^ memory buffer pointer+ , `Int' -- ^ memory buffer size in bytes+ , `Float' -- ^ volume+ } -> `Sample' toSample -- ^ returns handle+#}++-- | loads wav sound sample from memory buffer+sampleFromMemoryWav :: ByteString -- ^ wav sample data+ -> Float -- ^ volume+ -> IO Sample -- ^ return sample handle+sampleFromMemoryWav wavData volume = useAsCStringLen wavData $ \(ptr, size) -> _sampleFromMemoryWav ptr size volume++-- | loads ogg sound sample from memory buffer+sampleFromMemoryOgg :: ByteString -- ^ ogg sample data+ -> Float -- ^ volume+ -> IO Sample -- ^ return sample handle+sampleFromMemoryOgg oggData volume = useAsCStringLen oggData $ \(ptr, size) -> _sampleFromMemoryOgg ptr size volume++-- | loads a sound sample from file+{#fun sampleFromFile+ { `String' -- ^ sample filepath+ , `Float' -- ^ volume+ } -> `Sample' toSample -- ^ returns handle+#}++-- | set mixer volume+{#fun volume+ { `Float' -- ^ left+ , `Float' -- ^ right+ } -> `()'+#}++-- | number of currently active sounds {#fun soundActive {} -> `Int'#}++-- | stops all sounds immediately {#fun soundStopAll {} -> `()'#} -{#fun soundLoop {fromSample `Sample', `Float', `Float', `Float', `Float'} -> `()'#}-{#fun soundPlay {fromSample `Sample', `Float', `Float', `Float', `Float'} -> `()'#}-{#fun soundUpdate {fromSample `Sample', `Float', `Float', `Float', `Float'} -> `Bool'#}+-- | plays a specified sound sample continuously and sets its parameters+{#fun soundLoop+ { fromSample `Sample' -- ^ handle of a previously loaded sample+ , `Float' -- ^ left volume+ , `Float' -- ^ right volume+ , `Float' -- ^ time difference between left and right channel in seconds. Use negative values to specify a delay for the left channel, positive for the right+ , `Float' -- ^ pitch factor for playback. 0.5 corresponds to one octave below, 2.0 to one above the original sample+ } -> `()'+#}++-- | plays a specified sound sample once and sets its parameters+{#fun soundPlay+ { fromSample `Sample' -- ^ handle of a previously loaded sample+ , `Float' -- ^ left volume+ , `Float' -- ^ right volume+ , `Float' -- ^ time difference between left and right channel in seconds. Use negative values to specify a delay for the left channel, positive for the right+ , `Float' -- ^ pitch factor for playback. 0.5 corresponds to one octave below, 2.0 to one above the original sample+ } -> `()'+#}++-- | updates parameters of a specified sound+{#fun soundUpdate+ { fromSample `Sample' -- ^ handle of a currently active sound+ , `Float' -- ^ left volume+ , `Float' -- ^ right volume+ , `Float' -- ^ time difference between left and right channel in seconds. Use negative values to specify a delay for the left channel, positive for the right+ , `Float' -- ^ pitch factor for playback. 0.5 corresponds to one octave below, 2.0 to one above the original sample+ } -> `Bool' -- ^ return True in case the parameters have been updated successfully+#}++-- | stops a specified sound immediately {#fun soundStop {fromSample `Sample'} -> `Bool'#}
cbits/proAudioRt.cpp view
@@ -49,6 +49,12 @@ oParams.nChannels = 2; // stereo oParams.firstChannel = 0; + // initialize tracks:+ m_nSound=nTracks;+ ma_sound=new _AudioTrack[m_nSound];+ memset(ma_sound,0,m_nSound*sizeof(_AudioTrack));+ m_freqOut = frequency;+ try { m_dac.openStream( &oParams, NULL, RTAUDIO_SINT16, frequency, &chunkSize, &cbMix, (void *)this ); m_dac.startStream();@@ -59,11 +65,6 @@ return; } - // initialize tracks:- m_nSound=nTracks;- ma_sound=new _AudioTrack[m_nSound];- memset(ma_sound,0,m_nSound*sizeof(_AudioTrack));- m_freqOut = frequency; } DeviceAudioRt::~DeviceAudioRt() {
cbits/proteaaudio_binding.cpp view
@@ -82,7 +82,7 @@ return (int)audio.sampleFromFile(filename, volume); } -sample_t sampleFromMemoryWav(char *data, int size, float volume) {+sample_t _sampleFromMemoryWav(char *data, int size, float volume) { DeviceAudio & audio = DeviceAudio::singleton(); if((&audio) == 0) return 0; AudioSample* pSample = loadWavFromMemory((unsigned char*)data, size);@@ -92,7 +92,7 @@ return (int)ret; } -sample_t sampleFromMemoryOgg(char *data, int size, float volume) {+sample_t _sampleFromMemoryOgg(char *data, int size, float volume) { DeviceAudio & audio = DeviceAudio::singleton(); if((&audio) == 0) return 0; AudioSample* pSample = loadOggFromMemory((unsigned char*)data, size);
cbits/proteaaudio_binding.h view
@@ -8,8 +8,8 @@ void finishAudio(); int loaderAvailable(char* suffix); void volume(float left, float right);-sample_t sampleFromMemoryWav(char *data, int size, float volume);-sample_t sampleFromMemoryOgg(char *data, int size, float volume);+sample_t _sampleFromMemoryWav(char *data, int size, float volume);+sample_t _sampleFromMemoryOgg(char *data, int size, float volume); sample_t sampleFromFile(char* filename, float volume); int soundActive(); void soundStopAll();
example/play.hs view
@@ -6,22 +6,34 @@ import Sound.ProteaAudio +waitPayback = do+ n <- soundActive+ when (n > 0) $ do+ threadDelay 1000000+ waitPayback+ main = do args <- getArgs- initAudio 64 44100 1024- let fname = head args- -- load from file- smp <- sampleFromFile fname 1.0 -- volume+ filename <- case args of+ a : _ -> pure a+ _ -> fail "usage: proteaaudio-play SAMPLE_FILE_NAME" - buf <- SB.readFile fname- -- load from memory buffer- smp' <- case takeExtension fname of- ".ogg" -> SB.useAsCStringLen buf $ \(p,i) -> sampleFromMemoryOgg p i 1- ".wav" -> SB.useAsCStringLen buf $ \(p,i) -> sampleFromMemoryWav p i 1+ result <- initAudio 64 44100 1024 -- max channels, mixing frequency, mixing buffer size+ unless result $ fail "failed to initialize the audio system" - soundPlay smp' 1 1 0 1- let loop = do- n <- soundActive- when (n > 0) $ threadDelay 1000000 >> loop- loop+ -- (A) load sample from file+ sampleA <- sampleFromFile filename 1.0 -- volume++ soundPlay sampleA 1 1 0 1 -- left volume, right volume, time difference between left and right, pitch factor for playback+ waitPayback++ -- (B) load from memory buffer+ buffer <- SB.readFile filename+ sampleB <- case takeExtension filename of+ ".ogg" -> sampleFromMemoryOgg buffer 1.0+ ".wav" -> sampleFromMemoryWav buffer 1.0++ soundPlay sampleB 1 1 0 1 -- left volume, right volume, time difference between left and right, pitch factor for playback+ waitPayback+ finishAudio
proteaaudio.cabal view
@@ -1,5 +1,5 @@ Name: proteaaudio-Version: 0.6.5+Version: 0.7.0 Synopsis: Simple audio library for Windows, Linux, OSX. Description: Simple audio library for Windows, Linux, OSX. Supports Ogg and Wav playback and multichannel mixing. License: BSD3@@ -8,7 +8,7 @@ Maintainer: csaba.hruska@gmail.com Stability: Experimental Category: Sound-Tested-With: GHC == 7.10.3+Tested-With: GHC == 8.0.2 Cabal-Version: >= 1.10 Build-Type: Simple @@ -42,6 +42,9 @@ example/play.hs + README.md+ CHANGELOG.md+ Flag example Description: Build with example Default: False@@ -51,7 +54,9 @@ location: https://github.com/csabahruska/proteaaudio Library- Build-Depends: base >= 4 && < 5+ Build-Depends: base >= 4 && < 5,+ bytestring == 0.10.*+ default-language: Haskell2010 Build-tools: c2hs@@ -70,15 +75,13 @@ CC-Options: "-D__WINDOWS_DS__" Extra-Libraries: stdc++ ole32 dsound winmm if os(linux)- CC-Options: "-D__LINUX_ALSA__ -D__LINUX_PULSE__ -D__LINUX_OSS__"- Extra-Libraries: stdc++ pthread asound+ CC-Options: "-D__LINUX_PULSE__"+ Extra-Libraries: stdc++ pthread pulse-simple pulse if os(darwin) CC-Options: "-D__MACOSX_CORE__" Extra-Libraries: stdc++ pthread Frameworks: CoreFoundation CoreAudio - ghc-options: -O2- executable proteaaudio-play if flag(example) Buildable: True@@ -91,6 +94,6 @@ build-depends: base >= 4 && < 5,- filepath >=1.4 && <1.5,+ filepath >=1.4 && <2, bytestring == 0.10.*, proteaaudio