因此,在下面的代码中,我从笔记和组合和弦生成一个wav文件。我已经让它适用于单个音符和两个音符的和弦,但是对于超过2个音符的组合,我遇到了问题,因为我没有正常化频率。我知道我需要做什么<(将每帧的频率除以构成它的音符的数量),但不一定如何以优雅的方式(或以任何方式)。发生的事情是,我需要以某种方式获取notes''
返回的列表的长度,直到buildChord
,然后找出如何在输入到{{1 }}
我真的很茫然,在这里,所以任何意见都会非常感激。
buildChord
答案 0 :(得分:1)
关键问题在于功能:
buildChord :: [[Double]] -> WAVESamples
buildChord freqs = map ((:[]) . round . sum) $ transpose freqs
transpose freqs
的结果是每个正在播放的音符的特定时间点的音量列表(例如[45.2, 20, -10]
)。函数(:[] . round . sum)
首先将它们一起添加(例如55.2
),对其进行舍入(例如,到55
),并将其包装在列表中(例如[55]
)。 map (:[] . round . sum)
只是在所有时间实例中都这样做了。
问题是,如果您一次播放多个音符,则总和会产生一个音量太大的音符。更好的是取笔记的平均值,而不是总和。这意味着同时播放的10个音符不会太大声。令人惊讶的是,前奏中没有平均功能。所以我们可以编写自己的平均函数,或者只是将它嵌入传递给map的函数中。我做了后者,因为代码较少:
buildChord :: [[Double]] -> WAVESamples
buildChord freqs = map (\chord -> [round $ sum chord / genericLength chord]) $ transpose freqs
我猜你的问题是你正在写一个音乐制作程序,作为学习haskell的一种方式。我有一些想法可以使您的代码更容易调试,更多“像haskell”。
haskell中的代码通常被写为从输入到输出的一系列转换。那个buildChord函数就是一个很好的例子 - 首先输入被转置,然后用一个组合了多个声音幅度的函数进行映射。但是,您也可以用这种方式构建整个程序。
该程序的目的似乎是:“以某种格式从文件中读取注释,然后从读取的那些注释中创建一个wav文件”。我将解决这个问题的方法首先是将其分解为不同的纯转换(即不使用输入或输出),并将读取和写入作为最后一步。
我首先要将声波写入WAVE转换。我会使用类型:
data Sound = Sound { soundFreqs :: [Double]
, soundVolume :: Double
, soundLength :: Double
}
然后编写函数:
soundsToWAVE :: Int -> [Sound] -> WAVE
soundsToWAVE samplesPerSec sounds = undefined -- TODO
然后我可以编写函数writeSoundsToWavFile
和testPlaySounds
:
writeSoundsToWavFile :: String -> Int -> [Sound] -> IO ()
writeSoundsToWavFile fileN samplesPerSec sounds = putWAVEFile $ soundsToWAVE fileN samplesPerSec sounds
testPlaySounds :: [Sound] -> IO ()
testPlaySounds sounds = do
writeSoundsToWavFile "test.wav" 32000 sounds
system("afplay test.wav") -- use aplay on linux, don't know for windows
return ()
完成后,所有WAVE代码都完成了 - 其余代码不需要触摸它。将它放在自己的模块中可能是个好主意。
之后,我会在音乐笔记和声音之间进行转换。我会使用以下类型的笔记:
data Note = A | B | C | D | E | F | G
data NoteAugment = None | Sharp | Flat
data MusicNote = MusicNote { note :: Note, noteAugment :: NoteAugment, noteOctave :: Int }
data Chord = Chord { notes :: [MusicNote], chordVolume :: Double }
然后编写函数:
chordToSound :: Chord -> Sound
chordToSound = undefined -- TODO
然后您可以轻松编写函数musicNotesToWAVFile:
chordsToWAVFile fileName samplesPerSec notes = writeSoundsToWavFile 32000 fileName samplesPerSec (map chordToSound notes)
(函数testPlayChords可以以相同的方式完成)。你也可以将它放在一个新模块中。
最后我会写转换音符字符串 - &gt; [弦]。这只需要这个功能:
parseNoteFileText :: String -> [Chord]
parseNoteFileText noteText = undefined
然后可以连接最终的程序:
main = do
putStrLn "Enter the name of the file: "
fileN <- getLine
noteText <- readFile fileN
chordsToWAVFile (parseNoteFileText noteText)