Merge pull request #109 from cinaera/pr_shape

Shaped audio source support
This commit is contained in:
Dennis Felsing 2014-11-29 21:28:29 +01:00
commit 82eba3875d
8 changed files with 328 additions and 27 deletions

View file

@ -40,7 +40,7 @@ struct CChannel
{
int m_Vol;
int m_Pan;
} ;
};
struct CVoice
{
@ -49,10 +49,17 @@ struct CVoice
int m_Age; // increases when reused
int m_Tick;
int m_Vol; // 0 - 255
int m_FalloffDistance; // 0 - inifinitee (well int)
int m_Flags;
int m_X, m_Y;
} ;
float m_Falloff; // [0.0, 1.0]
int m_Shape;
union
{
ISound::CVoiceShapeCircle m_Circle;
ISound::CVoiceShapeRectangle m_Rectangle;
};
};
static CSample m_aSamples[NUM_SAMPLES] = { {0} };
static CVoice m_aVoices[NUM_VOICES] = { {0} };
@ -135,20 +142,79 @@ static void Mix(short *pFinalOut, unsigned Frames)
// TODO: we should respect the channel panning value
int dx = v->m_X - m_CenterX;
int dy = v->m_Y - m_CenterY;
int Dist = (int)sqrtf((float)dx*dx+dy*dy); // float here. nasty
//
int p = IntAbs(dx);
int Range = v->m_FalloffDistance;
if(Dist >= 0 && Dist < Range)
float FalloffX = 0.0f;
float FalloffY = 0.0f;
int RangeX = 0; // for panning
bool InVoiceField = false;
switch(v->m_Shape)
{
case ISound::SHAPE_CIRCLE:
{
float r = v->m_Circle.m_Radius;
RangeX = r;
int Dist = (int)sqrtf((float)dx*dx+dy*dy); // nasty float
if(Dist < r)
{
InVoiceField = true;
// falloff
int FalloffDistance = r*v->m_Falloff;
if(Dist > FalloffDistance)
FalloffX = FalloffY = (r-Dist)/(r-FalloffDistance);
else
FalloffX = FalloffY = 1.0f;
}
else
InVoiceField = false;
break;
}
case ISound::SHAPE_RECTANGLE:
{
RangeX = v->m_Rectangle.m_Width/2.0f;
int abs_dx = abs(dx);
int abs_dy = abs(dy);
int w = v->m_Rectangle.m_Width/2.0f;
int h = v->m_Rectangle.m_Height/2.0f;
if(abs_dx < w && abs_dy < h)
{
InVoiceField = true;
// falloff
int fx = v->m_Falloff * w;
int fy = v->m_Falloff * h;
FalloffX = abs_dx > fx ? (float)(w-abs_dx)/(w-fx) : 1.0f;
FalloffY = abs_dy > fy ? (float)(h-abs_dy)/(h-fy) : 1.0f;
}
else
InVoiceField = false;
break;
}
};
if(InVoiceField)
{
// panning
if(dx > 0)
Lvol = ((Range-p)*Lvol)/Range;
Lvol = ((RangeX-p)*Lvol)/RangeX;
else
Rvol = ((Range-p)*Rvol)/Range;
Rvol = ((RangeX-p)*Rvol)/RangeX;
// falloff
Lvol = (Lvol*(Range-Dist))/Range;
Rvol = (Rvol*(Range-Dist))/Range;
{
Lvol *= FalloffX;
Rvol *= FalloffY;
}
}
else
{
@ -646,7 +712,7 @@ void CSound::SetVoiceVolume(CVoiceHandle Voice, float Volume)
m_aVoices[VoiceID].m_Vol = (int)(Volume*255.0f);
}
void CSound::SetVoiceMaxDistance(CVoiceHandle Voice, int Distance)
void CSound::SetVoiceFalloff(CVoiceHandle Voice, float Falloff)
{
if(!Voice.IsValid())
return;
@ -656,10 +722,8 @@ void CSound::SetVoiceMaxDistance(CVoiceHandle Voice, int Distance)
if(m_aVoices[VoiceID].m_Age != Voice.Age())
return;
if(Distance < 0)
return;
m_aVoices[VoiceID].m_FalloffDistance = Distance;
Falloff = clamp(Falloff, 0.0f, 1.0f);
m_aVoices[VoiceID].m_Falloff = Falloff;
}
void CSound::SetVoiceLocation(CVoiceHandle Voice, float x, float y)
@ -713,6 +777,35 @@ void CSound::SetVoiceTimeOffset(CVoiceHandle Voice, float offset)
lock_release(m_SoundLock);
}
void CSound::SetVoiceCircle(CVoiceHandle Voice, float Radius)
{
if(!Voice.IsValid())
return;
int VoiceID = Voice.Id();
if(m_aVoices[VoiceID].m_Age != Voice.Age())
return;
m_aVoices[VoiceID].m_Shape = ISound::SHAPE_CIRCLE;
m_aVoices[VoiceID].m_Circle.m_Radius = max(0.0f, Radius);
}
void CSound::SetVoiceRectangle(CVoiceHandle Voice, float Width, float Height)
{
if(!Voice.IsValid())
return;
int VoiceID = Voice.Id();
if(m_aVoices[VoiceID].m_Age != Voice.Age())
return;
m_aVoices[VoiceID].m_Shape = ISound::SHAPE_RECTANGLE;
m_aVoices[VoiceID].m_Rectangle.m_Width = max(0.0f, Width);
m_aVoices[VoiceID].m_Rectangle.m_Height = max(0.0f, Height);
}
void CSound::SetChannel(int ChannelID, float Vol, float Pan)
{
m_aChannels[ChannelID].m_Vol = (int)(Vol*255.0f);
@ -752,7 +845,9 @@ ISound::CVoiceHandle CSound::Play(int ChannelID, int SampleID, int Flags, float
m_aVoices[VoiceID].m_Flags = Flags;
m_aVoices[VoiceID].m_X = (int)x;
m_aVoices[VoiceID].m_Y = (int)y;
m_aVoices[VoiceID].m_FalloffDistance = DefaultDistance;
m_aVoices[VoiceID].m_Falloff = 0.0f;
m_aVoices[VoiceID].m_Shape = ISound::SHAPE_CIRCLE;
m_aVoices[VoiceID].m_Circle.m_Radius = DefaultDistance;
Age = m_aVoices[VoiceID].m_Age;
}

View file

@ -41,10 +41,13 @@ public:
virtual void SetChannel(int ChannelID, float Vol, float Pan);
virtual void SetVoiceVolume(CVoiceHandle Voice, float Volume);
virtual void SetVoiceMaxDistance(CVoiceHandle Voice, int Distance);
virtual void SetVoiceFalloff(CVoiceHandle Voice, float Falloff);
virtual void SetVoiceLocation(CVoiceHandle Voice, float x, float y);
virtual void SetVoiceTimeOffset(CVoiceHandle Voice, float offset); // in s
virtual void SetVoiceCircle(CVoiceHandle Voice, float Radius);
virtual void SetVoiceRectangle(CVoiceHandle Voice, float Width, float Height);
CVoiceHandle Play(int ChannelID, int SampleID, int Flags, float x, float y);
virtual CVoiceHandle PlayAt(int ChannelID, int SampleID, int Flags, float x, float y);
virtual CVoiceHandle Play(int ChannelID, int SampleID, int Flags);

View file

@ -16,6 +16,23 @@ public:
FLAG_ALL=3
};
enum
{
SHAPE_CIRCLE,
SHAPE_RECTANGLE,
};
struct CVoiceShapeCircle
{
float m_Radius;
};
struct CVoiceShapeRectangle
{
float m_Width;
float m_Height;
};
class CVoiceHandle
{
friend class ISound;
@ -48,10 +65,13 @@ public:
virtual void SetListenerPos(float x, float y) = 0;
virtual void SetVoiceVolume(CVoiceHandle Voice, float Volume) = 0;
virtual void SetVoiceMaxDistance(CVoiceHandle Voice, int Distance) = 0;
virtual void SetVoiceFalloff(CVoiceHandle Voice, float Falloff) = 0;
virtual void SetVoiceLocation(CVoiceHandle Voice, float x, float y) = 0;
virtual void SetVoiceTimeOffset(CVoiceHandle Voice, float offset) = 0; // in s
virtual void SetVoiceCircle(CVoiceHandle Voice, float Radius) = 0;
virtual void SetVoiceRectangle(CVoiceHandle Voice, float Width, float Height) = 0;
virtual CVoiceHandle PlayAt(int ChannelID, int SampleID, int Flags, float x, float y) = 0;
virtual CVoiceHandle Play(int ChannelID, int SampleID, int Flags) = 0;
virtual void Stop(int SampleID) = 0;

View file

@ -63,6 +63,9 @@ void CMapSounds::OnMapLoad()
{
CMapItemLayerSounds *pSoundLayer = (CMapItemLayerSounds *)pLayer;
if(pSoundLayer->m_Version < 1 || pSoundLayer->m_Version > CMapItemLayerSounds::CURRENT_VERSION)
continue;
if(pSoundLayer->m_Sound == -1)
continue;
@ -119,8 +122,22 @@ void CMapSounds::OnRender()
if(pSource->m_pSource->m_Loop) Flags |= ISound::FLAG_LOOP;
pSource->m_Voice = m_pClient->m_pSounds->PlaySampleAt(CSounds::CHN_MAPSOUND, m_aSounds[pSource->m_Sound], 1.0f, vec2(fx2f(pSource->m_pSource->m_Position.x), fx2f(pSource->m_pSource->m_Position.y)), Flags);
Sound()->SetVoiceMaxDistance(pSource->m_Voice, pSource->m_pSource->m_FalloffDistance);
Sound()->SetVoiceTimeOffset(pSource->m_Voice, offset);
Sound()->SetVoiceFalloff(pSource->m_Voice, pSource->m_pSource->m_Falloff/255.0f);
switch(pSource->m_pSource->m_Shape.m_Type)
{
case CSoundShape::SHAPE_CIRCLE:
{
Sound()->SetVoiceCircle(pSource->m_Voice, pSource->m_pSource->m_Shape.m_Circle.m_Radius);
break;
}
case CSoundShape::SHAPE_RECTANGLE:
{
Sound()->SetVoiceRectangle(pSource->m_Voice, fx2f(pSource->m_pSource->m_Shape.m_Rectangle.m_Width), fx2f(pSource->m_pSource->m_Shape.m_Rectangle.m_Height));
break;
}
};
}
}
else
@ -149,6 +166,9 @@ void CMapSounds::OnRender()
if(pLayer->m_Type == LAYERTYPE_SOUNDS)
{
CMapItemLayerSounds *pSoundLayer = (CMapItemLayerSounds *)pLayer;
if(pSoundLayer->m_Version < 1 || pSoundLayer->m_Version > CMapItemLayerSounds::CURRENT_VERSION)
continue;
CSoundSource *pSources = (CSoundSource *)Layers()->Map()->GetDataSwapped(pSoundLayer->m_Data);

View file

@ -996,6 +996,9 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
else if(pLayerItem->m_Type == LAYERTYPE_SOUNDS)
{
CMapItemLayerSounds *pSoundsItem = (CMapItemLayerSounds *)pLayerItem;
if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION)
continue;
CLayerSounds *pSounds = new CLayerSounds;
pSounds->m_pEditor = m_pEditor;
pLayer = pSounds;

View file

@ -40,7 +40,33 @@ void CLayerSounds::Render()
OffsetY = aChannels[1];
}
m_pEditor->RenderTools()->DrawCircle(fx2f(pSource->m_Position.x)+OffsetX, fx2f(pSource->m_Position.y)+OffsetY, pSource->m_FalloffDistance, 32);
switch(pSource->m_Shape.m_Type)
{
case CSoundShape::SHAPE_CIRCLE:
{
m_pEditor->RenderTools()->DrawCircle(fx2f(pSource->m_Position.x)+OffsetX, fx2f(pSource->m_Position.y)+OffsetY,
pSource->m_Shape.m_Circle.m_Radius, 32);
float Falloff = ((float)pSource->m_Falloff/255.0f);
if(Falloff > 0.0f)
m_pEditor->RenderTools()->DrawCircle(fx2f(pSource->m_Position.x)+OffsetX, fx2f(pSource->m_Position.y)+OffsetY,
pSource->m_Shape.m_Circle.m_Radius*Falloff, 32);
break;
}
case CSoundShape::SHAPE_RECTANGLE:
{
float Width = fx2f(pSource->m_Shape.m_Rectangle.m_Width);
float Height = fx2f(pSource->m_Shape.m_Rectangle.m_Height);
m_pEditor->RenderTools()->DrawRoundRect(fx2f(pSource->m_Position.x)+OffsetX - Width/2, fx2f(pSource->m_Position.y)+OffsetY - Height/2,
Width, Height, 0.0f);
float Falloff = ((float)pSource->m_Falloff/255.0f);
if(Falloff > 0.0f)
m_pEditor->RenderTools()->DrawRoundRect(fx2f(pSource->m_Position.x)+OffsetX - Falloff*Width/2, fx2f(pSource->m_Position.y)+OffsetY - Falloff*Height/2,
Width*Falloff, Height*Falloff, 0.0f);
break;
}
}
}
Graphics()->QuadsEnd();
@ -84,13 +110,22 @@ CSoundSource *CLayerSounds::NewSource()
pSource->m_Loop = 1;
pSource->m_TimeDelay = 0;
pSource->m_FalloffDistance = 1500;
pSource->m_PosEnv = -1;
pSource->m_PosEnvOffset = 0;
pSource->m_SoundEnv = -1;
pSource->m_SoundEnvOffset = 0;
pSource->m_Falloff = 255;
/*
pSource->m_Shape.m_Type = CSoundShape::SHAPE_CIRCLE;
pSource->m_Shape.m_Circle.m_Radius = 1500;
*/
pSource->m_Shape.m_Type = CSoundShape::SHAPE_RECTANGLE;
pSource->m_Shape.m_Rectangle.m_Width = f2fx(1500.0f);
pSource->m_Shape.m_Rectangle.m_Height = f2fx(1000.0f);
return pSource;
}

View file

@ -624,13 +624,46 @@ int CEditor::PopupSource(CEditor *pEditor, CUIRect View)
return 1;
}
// Sound shape button
CUIRect ShapeButton;
View.HSplitBottom(3.0f, &View, 0x0);
View.HSplitBottom(12.0f, &View, &ShapeButton);
static int s_ShapeTypeButton = 0;
static const char *s_aShapeNames[] = {
"Rectangle",
"Circle"
};
if(pEditor->DoButton_Editor(&s_ShapeTypeButton, s_aShapeNames[pSource->m_Shape.m_Type], 0, &ShapeButton, 0, "Change shape"))
{
pSource->m_Shape.m_Type = (pSource->m_Shape.m_Type+1)%CSoundShape::NUM_SHAPES;
// set default values
switch(pSource->m_Shape.m_Type)
{
case CSoundShape::SHAPE_CIRCLE:
{
pSource->m_Shape.m_Circle.m_Radius = 1000.0f;
break;
}
case CSoundShape::SHAPE_RECTANGLE:
{
pSource->m_Shape.m_Rectangle.m_Width = f2fx(1000.0f);
pSource->m_Shape.m_Rectangle.m_Height = f2fx(800.0f);
break;
}
}
}
enum
{
PROP_POS_X=0,
PROP_POS_Y,
PROP_LOOP,
PROP_TIME_DELAY,
PROP_DISTANCE,
PROP_FALLOFF,
PROP_POS_ENV,
PROP_POS_ENV_OFFSET,
PROP_SOUND_ENV,
@ -643,7 +676,7 @@ int CEditor::PopupSource(CEditor *pEditor, CUIRect View)
{"Pos Y", pSource->m_Position.y/1000, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Loop", pSource->m_Loop, PROPTYPE_BOOL, 0, 1},
{"Delay", pSource->m_TimeDelay, PROPTYPE_INT_SCROLL, 0, 1000000},
{"Distance", pSource->m_FalloffDistance, PROPTYPE_INT_SCROLL, 0, 1000000},
{"Falloff", pSource->m_Falloff, PROPTYPE_INT_SCROLL, 0, 255},
{"Pos. Env", pSource->m_PosEnv+1, PROPTYPE_INT_STEP, 0, pEditor->m_Map.m_lEnvelopes.size()+1},
{"Pos. TO", pSource->m_PosEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Sound Env", pSource->m_SoundEnv+1, PROPTYPE_INT_STEP, 0, pEditor->m_Map.m_lEnvelopes.size()+1},
@ -652,6 +685,8 @@ int CEditor::PopupSource(CEditor *pEditor, CUIRect View)
{0},
};
static int s_aIds[NUM_PROPS] = {0};
int NewVal = 0;
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal);
@ -662,7 +697,7 @@ int CEditor::PopupSource(CEditor *pEditor, CUIRect View)
if(Prop == PROP_POS_Y) pSource->m_Position.y = NewVal*1000;
if(Prop == PROP_LOOP) pSource->m_Loop = NewVal;
if(Prop == PROP_TIME_DELAY) pSource->m_TimeDelay = NewVal;
if(Prop == PROP_DISTANCE) pSource->m_FalloffDistance = NewVal;
if(Prop == PROP_FALLOFF) pSource->m_Falloff = NewVal;
if(Prop == PROP_POS_ENV)
{
int Index = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1);
@ -694,6 +729,66 @@ int CEditor::PopupSource(CEditor *pEditor, CUIRect View)
}
if(Prop == PROP_SOUND_ENV_OFFSET) pSource->m_SoundEnvOffset = NewVal;
// source shape properties
switch(pSource->m_Shape.m_Type)
{
case CSoundShape::SHAPE_CIRCLE:
{
enum
{
PROP_CIRCLE_RADIUS=0,
NUM_CIRCLE_PROPS,
};
CProperty aCircleProps[] = {
{"Radius", pSource->m_Shape.m_Circle.m_Radius, PROPTYPE_INT_SCROLL, 0, 1000000},
{0},
};
static int s_aCircleIds[NUM_CIRCLE_PROPS] = {0};
NewVal = 0;
Prop = pEditor->DoProperties(&View, aCircleProps, s_aCircleIds, &NewVal);
if(Prop != -1)
pEditor->m_Map.m_Modified = true;
if(Prop == PROP_CIRCLE_RADIUS) pSource->m_Shape.m_Circle.m_Radius = NewVal;
break;
}
case CSoundShape::SHAPE_RECTANGLE:
{
enum
{
PROP_RECTANGLE_WIDTH=0,
PROP_RECTANGLE_HEIGHT,
NUM_RECTANGLE_PROPS,
};
CProperty aRectangleProps[] = {
{"Width", pSource->m_Shape.m_Rectangle.m_Width/1024, PROPTYPE_INT_SCROLL, 0, 1000000},
{"Height", pSource->m_Shape.m_Rectangle.m_Height/1024, PROPTYPE_INT_SCROLL, 0, 1000000},
{0},
};
static int s_aRectangleIds[NUM_RECTANGLE_PROPS] = {0};
NewVal = 0;
Prop = pEditor->DoProperties(&View, aRectangleProps, s_aRectangleIds, &NewVal);
if(Prop != -1)
pEditor->m_Map.m_Modified = true;
if(Prop == PROP_RECTANGLE_WIDTH) pSource->m_Shape.m_Rectangle.m_Width = NewVal*1024;
if(Prop == PROP_RECTANGLE_HEIGHT) pSource->m_Shape.m_Rectangle.m_Height = NewVal*1024;
break;
}
}
return 0;
}

View file

@ -18,6 +18,7 @@ enum
LAYERTYPE_SPEEDUP,
LAYERTYPE_SWITCH,
LAYERTYPE_TUNE,
LAYERTYPE_SOUNDS_DEPRECATED, // deprecated! do not use this, this is just for compatibility reasons
LAYERTYPE_SOUNDS,
MAPITEMTYPE_VERSION=0,
@ -329,23 +330,52 @@ struct CMapItemEnvelope : public CMapItemEnvelope_v1
int m_Synchronized;
};
struct CSoundShape
{
enum
{
SHAPE_RECTANGLE = 0,
SHAPE_CIRCLE,
NUM_SHAPES,
};
struct CRectangle
{
int m_Width, m_Height; // fxp 22.10
};
struct CCircle
{
int m_Radius;
};
int m_Type;
union
{
CRectangle m_Rectangle;
CCircle m_Circle;
};
};
struct CSoundSource
{
CPoint m_Position;
int m_Loop;
int m_TimeDelay; // in s
int m_FalloffDistance;
int m_Falloff; // [0,255] // 0 - No falloff, 255 - full
int m_PosEnv;
int m_PosEnvOffset;
int m_SoundEnv;
int m_SoundEnvOffset;
CSoundShape m_Shape;
};
struct CMapItemLayerSounds
{
enum { CURRENT_VERSION=1 };
enum { CURRENT_VERSION=2 };
CMapItemLayer m_Layer;
int m_Version;