Генерация звуковых данных .wav в Haskell - PullRequest
1 голос
/ 14 апреля 2011

Я пытаюсь программно сгенерировать файлы .wav из файла с форматом «Note Octave Note Octave» (например, 4 F # 1) в Haskell с использованием библиотеки Data.WAVE, и у меня возникла проблема: Iне могу понять, как именно рассчитать, что хранить в качестве заметок.На данный момент я пытаюсь сохранить их как синусоидальную волну, рассчитанную по частотам нот в октавах, но все, что я получаю из своих динамиков, это щелчки.Что я делаю не так, что это не генерация тонов?

import Data.WAVE
import Graphics.UI.SDL.Mixer.Samples

import Control.Applicative
import Data.List.Split (splitOn)
import Data.Char
import Data.Int (Int32)
import Data.List (group)
import System.IO (hGetContents, Handle, openFile, IOMode(..))

a4 = 440.0

frameRate = 16000

noteToFreq :: (String, Int) -> Double
noteToFreq (note, octave) =
    if octave >= -1 && octave < 10
    then if n /= 15.0
         then (2 ** (n + (12.0 * ((fromIntegral octave ::Double) - 4.0)))) * a4
         else error $ "Bad note: " ++ note
    else error $ "Bad octave: " ++ show octave
    where n = case note of
                "B#" -> -9.0
                "C"  -> -9.0
                "C#" -> -8.0
                "Db" -> -8.0
                "D"  -> -7.0
                "D#" -> -6.0
                "Eb" -> -6.0
                "E"  -> -5.0
                "Fb" -> -5.0
                "E#" -> -4.0
                "F"  -> -4.0
                "F#" -> -3.0
                "Gb" -> -3.0
                "G"  -> -2.0
                "G#" -> -1.0
                "Ab" -> -1.0
                "A"  -> 0.0
                "A#" -> 1.0
                "Bb" -> 1.0
                "B"  -> 2.0
                "Cb" -> 2.0
                _    -> 15.0

notesToSamples :: [(String, Int)] -> [WAVESample]
notesToSamples ns =
    map doubleToSample [sin $ pi * i * (f/fr) | i <- [0,0.1..len], f <- freqs]
    where freqs = map noteToFreq ns
          fr = fromIntegral frameRate :: Double
          len = fromIntegral (length ns) :: Double

getFileName :: IO FilePath
getFileName = putStr "Enter the name of the file: " >> getLine

openMFile :: IO Handle
openMFile = getFileName >>= \path -> 
            openFile path ReadMode

getNotesAndOctaves :: IO String
getNotesAndOctaves = openMFile >>= hGetContents

noteValuePairs :: String -> [(String, Int)]
noteValuePairs = pair . splitOn " "
    where pair (x:y:ys) = (x, read y) : pair ys
          pair []       = []

getWavSamples :: IO [WAVESample]
getWavSamples = (notesToSamples . noteValuePairs) <$> getNotesAndOctaves 

constructWAVE :: IO WAVE
constructWAVE = do
  samples <- map (:[]) . concatMap (replicate 1000) <$> getWavSamples
  let channels      = 1
      bitsPerSample = 32
      frames        = Just (length samples)
      header        =
          WAVEHeader channels frameRate bitsPerSample frames
  return $ WAVE header samples

makeWavFile :: IO ()
makeWavFile = constructWAVE >>= \wav -> putWAVEFile "temp.wav" wav

1 Ответ

3 голосов
/ 14 апреля 2011

Вот некоторый код для генерации тона с использованием этой библиотеки. Надеемся, что вы сможете использовать этот код с вашей собственной проблемой. Сначала проверьте, что он выдает правильную частоту для данного входа - я никогда не проверял это. Я на самом деле не проверял ваш код, так как большинство не имеет ничего общего с генерацией звука. С такой проблемой я обычно пытаюсь написать простейший код, необходимый для работы внешней библиотеки, прежде чем писать собственные абстракции:

module Sound where
import Data.WAVE
import Data.Int (Int32)
import Data.List.Split (splitOn)

samplesPS = 16000
bitrate = 32

header = WAVEHeader 1 samplesPS bitrate Nothing

sound :: Double  -- | Frequency
      -> Int -- | Samples per second
      -> Double -- | Lenght of sound in seconds
      -> Int32 -- | Volume, (maxBound :: Int32) for highest, 0 for lowest
      -> [Int32]
sound freq samples len volume = take (round $ len * (fromIntegral samples)) $ 
                         map (round . (* fromIntegral volume)) $
                         map sin [0.0, (freq * 2 * pi / (fromIntegral samples))..]

samples :: [[Int32]]
samples = map (:[]) $ sound 600 samplesPS 3 (maxBound `div` 2)

samples2 :: [[Int32]] -- play two tones at once
samples2 = map (:[]) $ zipWith (+) (sound 600 samplesPS 3 (maxBound `div` 2)) (sound 1000 samplesPS 3 (maxBound `div` 2))

waveData = WAVE header samples


makeWavFile :: WAVE -> IO ()
makeWavFile wav = putWAVEFile "temp.wav" wav

main = makeWavFile waveData

Как только вы начнете работать, вы можете написать и написать лучшую абстракцию. Вы должны быть в состоянии получить хорошую чистую абстракцию для этой библиотеки, поскольку единственная функция, которая использует IO, это та, которая записывает ее в файл.

...