我正在尝试用Haskell写一个输出音频的程序,我决定使用OpenAL作为音频后端,考虑到我使用OpenAL的经验很少,我想我要做的第一件事就是在Haskell中复制OpenAL programmer's guide中的示例代码,这是用C编写的。
下面是该指南中的C代码:
// Initialization
Device = alcOpenDevice(NULL); // select the "preferred device"
if (Device) {
Context=alcCreateContext(Device,NULL);
alcMakeContextCurrent(Context);
}
// Check for EAX 2.0 support
g_bEAX = alIsExtensionPresent("EAX2.0");
// Generate Buffers
alGetError(); // clear error code
alGenBuffers(NUM_BUFFERS, g_Buffers);
if ((error = alGetError()) != AL_NO_ERROR)
{
DisplayALError("alGenBuffers :", error);
return;
}
// Load test.wav
loadWAVFile("test.wav",&format,&data,&size,&freq,&loop);
if ((error = alGetError()) != AL_NO_ERROR)
{
DisplayALError("alutLoadWAVFile test.wav : ", error);
alDeleteBuffers(NUM_BUFFERS, g_Buffers);
return;
}
// Copy test.wav data into AL Buffer 0
alBufferData(g_Buffers[0],format,data,size,freq);
if ((error = alGetError()) != AL_NO_ERROR)
{
DisplayALError("alBufferData buffer 0 : ", error);
alDeleteBuffers(NUM_BUFFERS, g_Buffers);
return;
}
// Unload test.wav
unloadWAV(format,data,size,freq);
if ((error = alGetError()) != AL_NO_ERROR)
{
DisplayALError("alutUnloadWAV : ", error);
alDeleteBuffers(NUM_BUFFERS, g_Buffers);
return;
}
// Generate Sources
alGenSources(1,source);
if ((error = alGetError()) != AL_NO_ERROR)
{
DisplayALError("alGenSources 1 : ", error);
return;
}
// Attach buffer 0 to source
alSourcei(source[0], AL_BUFFER, g_Buffers[0]);
if ((error = alGetError()) != AL_NO_ERROR)
{
DisplayALError("alSourcei AL_BUFFER 0 : ", error);
}
// Exit
Context=alcGetCurrentContext();
Device=alcGetContextsDevice(Context);
alcMakeContextCurrent(NULL);
alcDestroyContext(Context);
alcCloseDevice(Device);
字符串
这是我目前为止得到的:
module Main where
import Data.Maybe ( fromJust )
import Data.StateVar ( ($=) )
import Sound.OpenAL ( genObjectNames )
import qualified Sound.OpenAL.AL as AL
import qualified Sound.OpenAL.ALC as ALC
main :: IO ()
main = do
-- Initialization
maybeDevice <- ALC.openDevice Nothing -- select the "preferred device"
let device = fromJust maybeDevice
maybeContext <- ALC.createContext device streamAttributes
ALC.currentContext $= maybeContext
-- Generate buffers
errors <- AL.alErrors -- clear error code
[g_buffers] <- genObjectNames n_buffers
-- Exit
deviceClosed <- ALC.closeDevice device
return ()
where streamAttributes = [ ALC.Frequency 44100,
ALC.MonoSources 1,
ALC.StereoSources 0 ]
n_buffers = 3
型
不幸的是,我在尝试生成缓冲区时遇到了一点障碍。目前,当我编译上述Haskell代码时,我得到以下错误:
app/Main.hs:19:20: error:
• No instance for (Data.ObjectName.GeneratableObjectName a0)
arising from a use of ‘genObjectNames’
• In a stmt of a 'do' block:
[g_buffers] <- genObjectNames n_buffers
In the expression:
do maybeDevice <- ALC.openDevice Nothing
let device = fromJust maybeDevice
maybeContext <- ALC.createContext device streamAttributes
ALC.currentContext $= maybeContext
....
In an equation for ‘main’:
main
= do maybeDevice <- ALC.openDevice Nothing
let device = ...
maybeContext <- ALC.createContext device streamAttributes
....
where
streamAttributes = [ALC.Frequency 44100, ....]
n_buffers = 3
|
19 | [g_buffers] <- genObjectNames n_buffers
| ^^^^^^^^^^^^^^
型
我对Haskell不是很有经验,所以我发现很难准确地指出我在这里做错了什么。我使用https://hackage.haskell.org/package/OpenAL来提供绑定。任何帮助都将不胜感激。
1条答案
按热度按时间ss2ws0br1#
快速回答是
genObjectNames
是一个重载函数,它可以生成缓冲区(通过C APIalGenBuffers
函数)和源(通过C APIalGenSources
函数)。程序中没有任何东西告诉GHC您想要哪一个,因为您没有在让GHC确定所需类型的上下文中使用g_buffers
。一种解决方法是添加一个显式类型。有几种方法可以做到这一点。一种方法不需要扩展,但有点迂回,因为它需要您提供
<-
语句右侧的类型,所以您必须指定完整的monadic类型:字符串
使用扩展,您可以直接指定返回类型,或者使用:
型
否则:
型
或者,您可以不指定类型,并在提供足够类型信息的上下文中使用
g_buffers
:型
作为对您收到的错误的更详细的解释,如果您从GHCi查询
genObjectNames
的类型,您将看到它的类型:型
这意味着
genObjectNames
在支持IO
的monad中运行,并为任何具有GeneratableObjectName
示例的a
类型返回列表[a]
。错误消息的这一部分:
型
告诉你GHC找不到某个未指定类型
a0
的GenerateableObjectName
示例。这是一个提示,表明GHC对所需类型的了解不够,无法找到你实际需要的GeneratableObjectName
示例。如果它说GeneratableObjectName Int
没有示例,那么你就会知道它 * 确实 * 有足够的类型信息来找到示例,但是由于代码中的一些错误,类型信息实际上是错误的,所以它正在寻找一个不可能存在的示例。您可以查询GHCi以确定哪些示例可用:
型
这表明
Buffer
和Source
都有这样的示例,并且将是返回类型的良好候选。