initial commit

This commit is contained in:
2014-01-18 15:06:11 +01:00
parent 3fabb8dcf8
commit 768bec39b3
408 changed files with 171325 additions and 2 deletions

View File

@@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BitBoxWin32", "BitBoxWin32\BitBoxWin32.vcproj", "{AB70AF1C-0C19-41FC-A6A0-3B89E3EC7868}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AB70AF1C-0C19-41FC-A6A0-3B89E3EC7868}.Debug|Win32.ActiveCfg = Debug|Win32
{AB70AF1C-0C19-41FC-A6A0-3B89E3EC7868}.Debug|Win32.Build.0 = Debug|Win32
{AB70AF1C-0C19-41FC-A6A0-3B89E3EC7868}.Release|Win32.ActiveCfg = Release|Win32
{AB70AF1C-0C19-41FC-A6A0-3B89E3EC7868}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,73 @@
#ifndef _3D_H_
#define _3D_H_
#include <nds.h>
// Commands
#define CMTXMODE 0x10
#define CMTXPUSH 0x11
#define CMTXPOP 0x12
#define CMTXIDENTITY 0x15
#define CMTXLOAD4x4 0x16
#define CMTXLOAD4x3 0x17
#define CMTXMULT3x3 0x1A
#define CMTXSCALE 0x1B
#define CMTXTRANSLATE 0x1C
#define CCOLOR 0x20
#define CNORMAL 0x21
#define CVERTEX 0x24
#define CPOLYATTR 0x29
#define CDIFAMB 0x30
#define CLIGHTVECTOR 0x32
#define CLIGHTCOLOR 0x33
#define CBEGIN 0x40
#define CVIEWPORT 0x60
// Parameters
#define PROJECTION 0
#define MODELVIEW 2
#define QUAD 1
#define NORMAL(x,y,z) (((x)&0x3FF)|(((y)&0x3FF)<<10)|(((z)&0x3FF)<<20))
#define VERTEX(x,y,z) \
(((x)&0x3FF)|(((y)&0x3FF)<<10)|(((z)&0x3FF)<<20))
#define DIFAMB(dif,amb) ((dif)|((amb)<<16))
#define VIEWPORT(x1,y1,x2,y2) ((x1)|((y1)<<8)|((x2)<<16)|((y2)<<24))
// Polygon attributes
#define LIGHT0 BIT(0)
#define POLYFRONT BIT(7)
#define POLYBACK BIT(6)
#define SOLID (31<<16)
#define ALPHA(a) ((a)<<16)
#define WIREFRAME (0<<16)
// GX FIFO command packer
#define COMMAND(a,b,c,d) \
(((a)&0xFF)|(((b)&0xFF)<<8)|(((c)&0xFF)<<16)|(((d)&0xFF)<<24))
void call_list(u32 * list, int size);
#define push() MATRIX_PUSH = 0
#define pop() MATRIX_POP = 1
#define identity() MATRIX_IDENTITY = 0
#define swap() GFX_FLUSH = 0
#define translate(x, y, z) \
MATRIX_TRANSLATE = x; \
MATRIX_TRANSLATE = y; \
MATRIX_TRANSLATE = z
#define scale(x, y, z) \
MATRIX_SCALE = x; \
MATRIX_SCALE = y; \
MATRIX_SCALE = z
void rotate(int angle, int v);
#define rotateX(angle) rotate(angle, 1)
#define rotateY(angle) rotate(angle, 2)
#define rotateZ(angle) rotate(angle, 3)
#endif // _3D_H_

View File

@@ -0,0 +1,267 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="BitBoxWin32"
ProjectGUID="{AB70AF1C-0C19-41FC-A6A0-3B89E3EC7868}"
RootNamespace="BitBoxWin32"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\tools\tracker\packages\portaudio\include"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="opengl32.lib portaudio_x86.lib sdl.lib sdlmain.lib"
LinkIncremental="2"
AdditionalLibraryDirectories="..\..\tools\tracker\packages\portaudio"
GenerateManifest="true"
GenerateDebugInformation="true"
SubSystem="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="portaudio\include"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="portaudio"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Fichiers sources"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\main.cpp"
>
</File>
<Filter
Name="Synth"
>
<File
RelativePath=".\channel.cpp"
>
</File>
<File
RelativePath=".\channel.h"
>
</File>
<File
RelativePath=".\midi.cpp"
>
</File>
<File
RelativePath=".\midi.h"
>
</File>
<File
RelativePath=".\psg.cpp"
>
</File>
<File
RelativePath=".\psg.h"
>
</File>
<File
RelativePath=".\synth.cpp"
>
</File>
<File
RelativePath=".\synth.h"
>
</File>
<File
RelativePath=".\tune.cpp"
>
</File>
<File
RelativePath=".\tune.h"
>
</File>
</Filter>
<Filter
Name="Video"
>
<File
RelativePath=".\3D.h"
>
</File>
<File
RelativePath=".\options.h"
>
</File>
<File
RelativePath=".\trig.c"
>
</File>
<File
RelativePath=".\trig.h"
>
</File>
</Filter>
</Filter>
<Filter
Name="Fichiers d&apos;en-t<>te"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Fichiers de ressources"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,36 @@
#include "channel.h"
Channel::Channel()
{
reset();
}
void Channel::reset()
{
duty = 0;
dec = 4;
for ( int i = 0 ; i < MAXROWS ; i++ ) {
notes[i].volume = 10;
notes[i].note = 60;
notes[i].active = false;
}
lastRow = 0;
}
void Channel::setNote(int pos, int volume, int note)
{
notes[pos].volume = volume;
notes[pos].note = note;
notes[pos].active = true;
if ( pos > lastRow )
lastRow = pos;
}
void Channel::deleteNote(int pos)
{
notes[pos].active = false;
if ( pos == lastRow )
while ( lastRow > 0 && notes[lastRow].active == false )
lastRow--;
}

View File

@@ -0,0 +1,28 @@
#ifndef _CHANNEL_H_
#define _CHANNEL_H_
#define MAXROWS 2048
struct Note {
int volume;
int note; // midi note number
bool active;
};
struct Channel {
int duty;
int dec;
Note notes[MAXROWS];
int lastRow;
Channel();
void reset();
void setNote(int pos, int volume, int note);
void setNote(int pos, Note & note) { setNote(pos, note.volume, note.note); }
void deleteNote(int pos);
};
#endif // _CHANNEL_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,692 @@
#include <SDL.h>
#include <windows.h>
#include <GL/gl.h>
#include "trig.h"
#include "glext.h"
#include "tune.h"
#include "synth.h"
#ifdef _WINDOWS
#include <windows.h>
#endif
#define WIDTH 800
#define HEIGHT 600
Uint32 timer(Uint32 interval, void * param)
{
Synth * synth = (Synth *) param;
static int lastRow = -42;
int currentRow = synth->getCurrentRow();
if ( currentRow != lastRow ) {
lastRow = currentRow;
}
// Detect end of song and stop synth
int end = 0;
for ( int i = 0 ; i < 8 ; i++ )
if ( currentRow > synth->getTune()->channels[i].lastRow
&& synth->getVolume(i) == 0 )
end++;
else
break;
if ( end == 8 )
synth->stop(false);
return interval;
}
PFNGLCREATESHADERPROC glCreateShader = 0;
PFNGLCREATEPROGRAMPROC glCreateProgram = 0;
PFNGLLINKPROGRAMPROC glLinkProgram = 0;
PFNGLUSEPROGRAMPROC glUseProgram = 0;
PFNGLDETACHSHADERPROC glDetachShader = 0;
PFNGLATTACHSHADERPROC glAttachShader = 0;
PFNGLDELETEPROGRAMPROC glDeleteProgram = 0;
PFNGLDELETESHADERPROC glDeleteShader = 0;
PFNGLSHADERSOURCEPROC glShaderSource = 0;
PFNGLCOMPILESHADERPROC glCompileShader = 0;
GLuint fsh;
const GLchar* fshSource = "void main()\
{\
ivec3 i = ivec3(gl_Color * 63.0);\
gl_FragColor = vec4(ivec3(vec3(i/63.0) * 31.0) / 31.0, 1.0);\
}";
GLuint program;
void InitVideo()
{
glClearColor(0, 0, 0, 1.0f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
//glEnable(GL_POLYGON_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glEnable(GL_CULL_FACE);
glPolygonMode(GL_FRONT, GL_FILL);
glCullFace(GL_BACK);
glMatrixMode(GL_PROJECTION);
float matrix[4*4] =
{
6587/4096.0f, 0, 0, 0,
0, 8783/4096.0f, 0, 0,
0, 0, -4116/4096.0f, -4096/4096.0f,
0, 0, -821/4096.0f, 0,
};
glLoadMatrixf(matrix);
glMatrixMode(GL_MODELVIEW);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
static float diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
static float position[] = { 2.0f, 4.0f, 2.0f };
glLightfv(GL_LIGHT0, GL_POSITION, position);
glCreateShader = (PFNGLCREATESHADERPROC) wglGetProcAddress("glCreateShader");
glCreateProgram = (PFNGLCREATEPROGRAMPROC) wglGetProcAddress("glCreateProgram");
glLinkProgram = (PFNGLLINKPROGRAMPROC) wglGetProcAddress("glLinkProgram");
glUseProgram = (PFNGLUSEPROGRAMPROC) wglGetProcAddress("glUseProgram");
glDetachShader = (PFNGLDETACHSHADERPROC) wglGetProcAddress("glDetachShader");
glAttachShader = (PFNGLATTACHSHADERPROC) wglGetProcAddress("glAttachShader");
glDeleteProgram = (PFNGLDELETEPROGRAMPROC) wglGetProcAddress("glDeleteProgram");
glDeleteShader = (PFNGLDELETESHADERPROC) wglGetProcAddress("glDeleteShader");
glShaderSource = (PFNGLSHADERSOURCEPROC) wglGetProcAddress("glShaderSource");
glCompileShader = (PFNGLCOMPILESHADERPROC) wglGetProcAddress("glCompileShader");
fsh = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fsh, 1, & fshSource, 0);
glCompileShader(fsh);
program = glCreateProgram();
glAttachShader(program, fsh);
glLinkProgram(program);
}
int fade_current = 31 << 2; // starts white
int fade_target = 16 << 2;
void cube()
{
glUseProgram(program);
glDisable(GL_BLEND);
glColor3f(0.5f, 0.5f, 0.5f);
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glNormal3f(0.0f, 0.0f, -1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, -1.0f, -1.0f);
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f( 1.0f, -1.0f, -1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glNormal3f(0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
glNormal3f(-1.0f, 0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glNormal3f(1.0f, 0.0f, 0.0f);
glVertex3f( 1.0f, -1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
glEnd();
glUseProgram(0);
}
#define RGB15(r, g, b) ((r)|((g)<<5)|((b)<<10))
#define RED(rgb) ( rgb & 0x1F )
#define GREEN(rgb) ((rgb>>5)&0x1F)
#define BLUE(rgb) ((rgb>>10)&0x1F)
#define identity glLoadIdentity
#define push glPushMatrix
#define pop glPopMatrix
#define translate(x, y, z) glTranslatef((x) / 4096.0f, (y) / 4096.0f, (z) / 4096.0f)
#define scale(x, y, z) glScalef((x) / 4096.0f, (y) / 4096.0f, (z) / 4096.0f)
#define rotateX(a) rotate(a, 0)
#define rotateY(a) rotate(a, 1)
#define rotateZ(a) rotate(a, 2)
void rotate(int a, int v)
{
float angle = a / 12867.0f * 180.0f;
if ( v == 1 )
glRotatef(angle, 0, 1, 0);
else if ( v == 2 )
glRotatef(angle, 0, 0, 1);
else
glRotatef(angle, 1, 0, 0);
}
int uber_sin(int x)
{
int ax;
x = 4096 - (x & 0x1FFF);
ax = x;
if ( ax < 0 )
ax = -ax;
return 4 * x - ((4 * x * ax) >> 12);
}
void arms(int t, int m)
{
push();
// translate(0.24f*m, 0.4f, 0);
translate(983*m, 1638, 0);
// rotateZ(30<33>*m);
rotateZ(2145*m);
// rotateX(cos(t)*20<32>+180<38>);
rotateX(((cos(t)*1430)>>12)+12868);
// translate(0, 0.3, 0);
translate(0, 1229, 0);
push();
// scale(0.1, 0.3, 0.1);
scale(410, 1229, 410);
cube();
pop();
// translate(0, 0.22, 0);
translate(0, 901, 0);
// rotateY(abs(cos(t))*16*m);
int act = cos(t)*1144;
if ( act < 0 )
act = -act;
rotateY((act>>12)*m);
// rotateX(100<30>);
rotateX(7149);
// rotateZ(165<36>*m);
rotateZ(11796*m);
// translate(0, 0.21, 0);
translate(0, 860, 0);
push();
// scale(0.09, 0.27, 0.09);
scale(369, 1106, 369);
cube();
pop();
pop();
}
void legs(int t, int m)
{
push();
translate(778*m, 0, -61);
rotateX(((cos(t)*929)>>12)-228); // 929->1024 = -12 bytes !
translate(0, -2867, 0);
push();
scale(410, 1024, 410);
cube();
pop();
int d = -cos(t+6434);
if ( d < 0 )
d = 0;
translate(0, ((d*512)>>12)-2130, ((d*61)>>12)-246);
rotateX(715);
push();
scale(410, 1229, 410);
cube();
pop();
translate(0, -1229, 410);
push();
scale(410, 123, 819);
cube();
pop();
pop();
}
void robot(int t, int T)
{
#define robot_fall_speed 2048
#define robot_start 40
t *= 402;
// ast = |sin(t)|
int ast = sin(t);
if ( ast < 0 )
ast = -ast;
// act = |cos(t)|
int act = cos(t);
if ( act < 0 )
act = -act;
int fall;
// head
push();
// translate(0, 0.71+0.05*abs(sin(t)), 0.04);
fall = (robot_start+88)*robot_fall_speed - T*robot_fall_speed;
if ( fall < 0 )
fall = 0;
translate(0, 2908+((ast*204)>>12)+fall, 164);
// rotateX(-5<>+10<31>*abs(cos(t)));
rotateX((-357+715*act)>>12);
// scale(0.2, 0.2, 0.2);
scale(819, 819, 819);
cube();
pop();
// body
push();
// translate(0, 0.05*abs(sin(t)), 0);
fall = (robot_start+48)*robot_fall_speed - T*robot_fall_speed;
if ( fall < 0 )
fall = 0;
translate(0, ((ast*204)>>12)+fall, 0);
// rotateY(2.5<EFBFBD>-5<>*cos(t));
rotateY(179-((357*cos(t))>>12));
// scale(0.3, 0.5, 0.17);
scale(1229, 2048, 696);
cube();
pop();
// left side
push();
fall = (robot_start+56)*robot_fall_speed - T*robot_fall_speed;
if ( fall < 0 )
fall = 0;
translate(0, fall, 0);
arms(t, -1);
pop();
push();
fall = (robot_start+8)*robot_fall_speed - T*robot_fall_speed;
if ( fall < 0 )
fall = 0;
translate(0, fall, 0);
legs(t, -1);
pop();
// right side
t += 12868;
push();
fall = (robot_start+64)*robot_fall_speed - T*robot_fall_speed;
if ( fall < 0 )
fall = 0;
translate(0, fall, 0);
arms(t, 1);
pop();
push();
fall = (robot_start+24)*robot_fall_speed - T*robot_fall_speed;
if ( fall < 0 )
fall = 0;
translate(0, fall, 0);
legs(t, 1);
pop();
}
// r * cos((x*pi/2)/r) * cos((y*pi/2)/r) - r
// returns a fixed point altitude value in fx.6
int f(int x, int y)
{
const int r = 32;
// const int _r = (int)((0.5f/(float)r)*64.0f); // == 1 ...
// x = (x*_r);
// y = (y*_r);
return ((r * (uber_cos(x) * uber_cos(y))>>18)) - (r<<6);
}
void sonic(int t)
{
int i = 0;
int y, x;
rotateX(-1024);
if ( t < 256 )
t = 0;
else
t = -(t*2)&127;
glBegin(GL_QUADS);
for ( y = -7*64+t ; y < 6*64+t ; y += 64 ) {
for ( x = -7*64 ; x < 6*64 ; x += 64 ) {
if ( i & 1 )
glColor3f(29/31.0f, 15/31.0f, 4/31.0f);
else
glColor3f(4/31.0f, 11/31.0f, 28/31.0f);
glVertex3f(x / 64.0f, f(x, y)/64.0f, y / 64.0f);
glVertex3f(x / 64.0f, f(x, y+64)/64.0f, y / 64.0f + 1.0f);
glVertex3f(x / 64.0f + 1.0f, f(x+64, y+64)/64.0f, y / 64.0f + 1.0f);
glVertex3f(x / 64.0f + 1.0f, f(x+64, y)/64.0f, y / 64.0f);
i++;
}
}
glEnd();
}
void spot(int c1, int c2, int w, int h)
{
float c1r = RED(c1)/31.0f, c1g = GREEN(c1)/31.0f, c1b = BLUE(c1)/31.0f;
float c2r = RED(c2)/31.0f, c2g = GREEN(c2)/31.0f, c2b = BLUE(c2)/31.0f;
glBegin(GL_QUADS);
glColor3f(c1r, c1g, c1b);
glVertex3f(-1.0f, h/64.0f, 0.0f);
glVertex3f(1.0f, h/64.0f, 0.0f);
glColor3f(c2r, c2g, c2b);
glVertex3f(1.0f + w / 64.0f, 1.0f, 0.0f);
glVertex3f(-1.0f-w/64.0f, 1.0f, 0.0f);
glEnd();
}
void robot_solo(int t)
{
int T = t;
if ( t < 256 ) {
// stopped for the intro
t = 0;
} else if ( t > 1152 ) {
// tracted by the flying saucer
int y;
y = (t-1152)*58;
if ( y > 22528 )
y = 22528;
translate(0, y, 0);
y = (8*4096-y)/8;
scale(y, y, y);
t = 0;
}
robot(t, T);
}
int jump(int t)
{
if ( t < 512 )
return 0;
t = t%64;
t = t*8-384+128;
t = 3072-t*t;
if ( t < 0 )
return 0;
return t;
}
void robot_clone_wars(int t)
{
#define SPACE_FLOOR 1
#define FINAL_WALK_ON 1
#define LINES 6
#define SPACING 16384
int i, j;
//rotateX(2560);
if ( (t & 1) == 0 ) { // different camera for different screens
translate(5120, 44032, 20480);
rotateZ(1792);
} else {
rotateX(2560);
}
push();
translate(0, -6144, -4096*4);
rotateX(-12867/2);
scale(4096*16, 4096*10, 4096*10);
spot(RGB15(0, 7, 3), RGB15(0, 2, 1), 0, -64);
pop();
int z = t*256-16384;
if ( z > 0 )
z = 0;
int lines = (t-192+8)/8;
if ( lines < 2 )
lines = 2;
else if ( lines > LINES )
lines = LINES;
for ( i = 1 ; i < lines ; i++ ) {
translate(0, 0, -8192);
for ( j = 0 ; j < i ; j++ ) {
if ( j == 2 && i == 5 )
continue;
push();
translate(j * SPACING - ((i-1)*SPACING)/2, jump(t-i*4)+z, 0);
if ( t < 512 )
robot(8, 256);
else
robot(8+t-512, 256);
pop();
}
}
}
void background(int t)
{
glDisable(GL_BLEND);
if ( t == 1536 ) {
fade_current = 0;
fade_target = 16<<2;
}
if ( t < 1536 )
spot(RGB15(31, 31, 31), RGB15(12, 12, 31), 0, -64);
else
spot(RGB15(1, 5, 2), RGB15(0, 0, 0), 0, -64);
}
void fs(int t)
{
static int h[9] = { 64, 58, 51, 32, 26, 19, 10, 1, 0 };
static int r[9] = { 0, 819, 1229, 1638, 3276, 4096, 3276, 2048, 0 };
int y = 1024*32-t*32;
if ( y < 0 )
y = 0;
translate(y, y, -y);
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
int i, j;
glBegin(GL_QUADS);
glColor3f(0.5f, 0.5f, 0.5f);
for ( i = 0 ; i < 8 ; i++ ) {
for ( j = 0 ; j < 8192 ; j += 512 ) {
glNormal3f(((uber_cos(j) * r[i])>>18) / 64.0f, h[i]/64.0f, ((uber_sin(j) * r[i])>>18)/64.0f);
glVertex3f(((uber_cos(j) * r[i])>>18) / 64.0f, h[i]/64.0f, ((uber_sin(j) * r[i])>>18)/64.0f);
glVertex3f(((uber_cos(j+512) * r[i])>>18) / 64.0f, h[i]/64.0f, ((uber_sin(j+512) * r[i])>>18)/64.0f);
glVertex3f(((uber_cos(j+512) * r[i+1])>>18) / 64.0f, h[i+1]/64.0f, ((uber_sin(j+512) * r[i+1])>>18)/64.0f);
glVertex3f(((uber_cos(j) * r[i+1])>>18) / 64.0f, h[i+1]/64.0f, ((uber_sin(j) * r[i+1])>>18)/64.0f);
}
}
glEnd();
glEnable(GL_CULL_FACE);
}
void fs_beam(int t)
{
int h = 64+8+384-t*4;
if ( h < 0 )
h = 0;
//GFX_POLY_FORMAT = LIGHT0|POLYFRONT|ALPHA(15);
glEnable(GL_BLEND);
spot(RGB15(0, 31, 4), RGB15(0, 31, 4), -32, h-384);
}
typedef short s16;
typedef char s8;
typedef struct {
void (*draw)(int time);
s16 start, end;
s8 ty, tz;
s8 s;
} Drawable;
Drawable demo[] =
{
// draw start, end ty, tz scale
{ robot_solo, 0, 1536, 1, -15, 4 },
{ sonic, 0, 1536, -8, -24, 4 },
{ fs, 0, 1536, 21, -15, 4 },
{ robot_clone_wars, 1536, 2576, -4, -16, 2 },
{ background, 0, 2576, 9, -44, 32 },
{ fs_beam, 1024, 1536, 18, -13, 4 },
};
int mouseX, mouseY;
void Render(int t)
{
if ( t < 2576 ) {
if ( fade_current < fade_target )
fade_current++;
else if ( fade_current > fade_target )
fade_current--;
//int v = 0;
//if ( fade_current < (16 << 2) )
// v = DARK | (16 - (fade_current >> 2));
//else if ( fade_current > (16 << 2) )
// v = BRIGHT | ((fade_current >> 2) - 16);
//REG_MASTER_BRIGHT = v;
//REG_MASTER_BRIGHT_SUB = v;
identity();
// Dual screen 3D
if ( t&1 ) {
glViewport(WIDTH/2 - 256/2, HEIGHT/2 - 384/2 - 96/2, 255, 191);
} else {
translate(0, -5*4096, 0);
glViewport(WIDTH/2 - 256/2, HEIGHT/2 - 384/2 + 192 - 96/2 + 96, 255, 191);
}
// Lightweight 3D player -olol
int i;
for ( i = 0 ; i < sizeof(demo)/sizeof(Drawable) ; i++ ) {
if ( t >= demo[i].start && t < demo[i].end ) {
push();
translate(0, demo[i].ty*1024, demo[i].tz*1024);
scale(demo[i].s*1024, demo[i].s*1024, demo[i].s*1024);
demo[i].draw(t-demo[i].start);
pop();
}
}
}
}
int main(int argc, char ** argv)
{
SDL_Event event;
bool running = true;
int t = 0;
#ifdef _WINDOWS
#ifdef _DEBUG
AllocConsole();
// freopen("CONOUT$", "wb", stderr);
// freopen("CONOUT$", "wb", stdout);
#endif
#endif
SDL_Init(SDL_INIT_EVERYTHING);
Tune * tune = new Tune();
Synth * synth = new Synth(tune);
if ( ! SDL_AddTimer(100, timer, synth) )
return 42;
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 1);
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 2);
SDL_Surface * screen = SDL_SetVideoMode(WIDTH, HEIGHT, 16, SDL_DOUBLEBUF|SDL_HWSURFACE|SDL_OPENGL);
SDL_WM_SetCaption("BitBox - Winport", "BitBox - Winport");
InitVideo();
synth->play();
tune->Load("plop");
Uint32 start = SDL_GetTicks();
while ( running ) {
while ( SDL_PollEvent(& event) )
{
switch ( event.type ) {
case SDL_MOUSEMOTION:
mouseX = event.motion.x;
mouseY = event.motion.y;
break;
case SDL_QUIT:
running = false;
break;
case SDL_VIDEORESIZE:
case SDL_VIDEOEXPOSE:
break;
case SDL_KEYDOWN:
if ( event.key.keysym.sym == SDLK_ESCAPE ) // Quit...
running = false;
break;
default:
break;
}
}
Uint32 now = SDL_GetTicks();
t = (now - start) / 16;
glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
Render(t);
Render(t+1);
SDL_GL_SwapBuffers();
}
delete synth;
delete tune;
SDL_FreeSurface(screen);
SDL_Quit();
#ifdef __WIN32__
#ifdef _DEBUG
FreeConsole();
#endif
#endif
return 0;
}

View File

@@ -0,0 +1,42 @@
#include "midi.h"
#include "math.h"
#include <stdio.h>
#ifdef _WINDOWS
int round(float f)
{
return (int) (f + 0.5f);
}
#endif
int midiNoteToFrequency(int note)
{
return (int) (440.0f * pow(2.0f, (note - 69)/12.0f));
}
int midiFrequencyToNote(int freq)
{
return round(69.0f + log(freq/440.0f)*17.31234f);
}
char * midiNoteName(int note)
{
static char str[4];
int l = note % 12;
str[0] = "CCDDEFFGGAAB"[l];
if ( l == 1 || l == 3 || l == 6 || l == 8 || l == 10 )
str[1] = '#';
else
str[1] = ' ';
int octave = (note - 12) / 12;
if ( note < 12 )
str[2] = '-';
else
str[2] = '0' + octave;
return str;
}

View File

@@ -0,0 +1,8 @@
#ifndef _MIDI_H_
#define _MIDI_H_
int midiFrequencyToNote(int freq);
int midiNoteToFrequency(int note);
char * midiNoteName(int note);
#endif // _MIDI_H_

View File

@@ -0,0 +1,8 @@
#ifndef _OPTIONS_H_
#define _OPTIONS_H_
#define STDLIB 0
#define CAMERA 0
#define SAFE 0
#endif // _OPTIONS_H_

View File

@@ -0,0 +1,76 @@
#include "psg.h"
PSG::PSG()
: sample(0), t(0.0f), volume(0), frequency(0), duty(0)
{
}
signed short PSG::compute(float time)
{
t += time;
if ( t >= 1.0f/(float)frequency ) {
sample = generate() * volume / 127;
t -= 1.0f/(float)frequency;
}
return sample;
}
void PSG::reset()
{
t = 0.0f;
sample = 0;
}
// NDS PSG Noise
// X=X SHR 1, IF carry THEN Out=LOW, X=X XOR 6000h ELSE Out=HIGH
signed short Noise::generate()
{
int carry;
carry = x & 1;
x >>= 1;
if ( carry ) {
x = x ^ 0x6000;
return -0x7FFF;
} else {
return 0x7FFF;
}
}
void Noise::reset()
{
x = 0x7FFF;
PSG::reset();
}
// NDS Wave Duty
// Each duty cycle consists of eight HIGH or LOW samples, so the sound
// frequency is 1/8th of the selected sample rate. The duty cycle always
// starts at the begin of the LOW period when the sound gets (re-)started.
//
// 0 12.5% "_______-_______-_______-"
// 1 25.0% "______--______--______--"
// 2 37.5% "_____---_____---_____---"
// 3 50.0% "____----____----____----"
// 4 62.5% "___-----___-----___-----"
// 5 75.0% "__------__------__------"
// 6 87.5% "_-------_-------_-------"
// 7 0.0% "________________________"
signed short WaveDuty::generate()
{
x = (x+1)%8;
if ( x <= duty )
return -0x7FFF;
else
return 0x7FFF;
}
void WaveDuty::reset()
{
x = 0;
PSG::reset();
}

View File

@@ -0,0 +1,47 @@
#ifndef _PSG_H_
#define _PSG_H_
class PSG {
signed short sample;
float t;
int volume;
int frequency;
protected:
int x;
int duty;
virtual signed short generate() = 0;
public:
PSG();
void setDuty(int d) { duty = d; }
void setVolume(int v) { volume = v; }
void setFrequency(int f) { frequency = f; t = 0.0f; sample = 0; }
virtual void reset();
signed short compute(float time);
signed short getSample() const { return sample; }
};
class Noise : public PSG {
signed short generate();
void reset();
public:
Noise() { reset(); }
};
class WaveDuty : public PSG {
signed short generate();
void reset();
public:
WaveDuty() { reset(); }
};
#endif // _PSG_H_

View File

@@ -0,0 +1,292 @@
#include "synth.h"
#include <stdio.h>
#include <string.h>
#include <SDL.h>
#include "midi.h"
int SynthStreamCallback(const void * in, void * out, unsigned long frames,
const PaStreamCallbackTimeInfo * timeInfo, PaStreamCallbackFlags status,
void * user)
{
Synth * synth = (Synth *) user;
synth->synth((signed short *) out, frames);
return 0;
}
Synth::Synth(Tune * tune)
: stream(0), tune(tune), dRes(0.0f), dRow(0.0f),
row(0), initialRow(0), latency(0.0), analyseBufferPosition(0)
{
// Initialize PortAudio
PaError code;
code = Pa_Initialize();
if ( code != paNoError )
printf("PortAudio error : %s\n", Pa_GetErrorText(code));
#ifdef _WINDOWS
// Find device with lowest latency
const PaDeviceInfo * deviceInfo;
PaDeviceIndex device = -1;
for ( PaDeviceIndex i = 0 ; i < Pa_GetDeviceCount() ; i++ ) {
deviceInfo = Pa_GetDeviceInfo(i);
if ( device == -1 ||
( deviceInfo->defaultLowOutputLatency < Pa_GetDeviceInfo(device)->defaultLowOutputLatency
&& deviceInfo->maxOutputChannels >= 2 ) )
device = i;
}
// Open device
PaStreamParameters param;
memset(& param, 0, sizeof(PaStreamParameters));
param.channelCount = 2;
param.device = device;
param.hostApiSpecificStreamInfo = 0;
param.sampleFormat = paInt16;
param.suggestedLatency = Pa_GetDeviceInfo(device)->defaultLowOutputLatency;
code = Pa_OpenStream(& stream, 0, & param, 44100.0, 512, paNoFlag, SynthStreamCallback, this);
if ( code != paNoError )
fprintf(stderr, "PortAudio error : %s\n", Pa_GetErrorText(code));
#else
Pa_OpenDefaultStream(& stream, 0, 2, paInt16, 44100.0, 512,
SynthStreamCallback, this);
#endif
if ( ! stream )
fprintf(stderr, "Could not init PortAudio\n");
#ifdef _DEBUG
if ( stream )
latency = Pa_GetStreamInfo(stream)->outputLatency;
fprintf(stderr, "Device latency : %fs.\n", latency);
#endif
// Initialize PSGs
for ( int i = 0 ; i < 6 ; i++ ) {
channels[i].psg = new WaveDuty();
channels[i].volume = 0;
channels[i].mute = false;
}
for ( int i = 6 ; i < 8 ; i++ ) {
channels[i].psg = new Noise();
channels[i].volume = 0;
channels[i].mute = false;
}
}
Synth::~Synth()
{
if ( stream ) {
Pa_CloseStream(stream);
Pa_Terminate();
}
}
void Synth::readRow()
{
for ( int i = 0 ; i < 8 ; i++ ) {
if ( tune->channels[i].notes[row].active ) {
channels[i].volume =
tune->channels[i].notes[row].volume << 4;
int freq = midiNoteToFrequency(tune->channels[i].notes[row].note);
if ( i < 6 )
freq *= 8;
channels[i].psg->setFrequency(freq);
}
}
}
void Synth::synth(signed short * buffer, int samples, bool analyse)
{
int left, right, s;
for ( int i = samples ; i > 0 ; i-- ) {
// Playing
if ( row < MAXROWS && dRes > 1.0f/(float)tune->resolution ) {
dRow += 1.0f/((float)tune->resolution * 60.0f);
if ( dRow > 1.0f/(float)tune->rpm && row < MAXROWS ) {
readRow();
row++;
dRow = 0.0f;
}
// Linear enveloppe
for ( int i = 0 ; i < 8 ; i++ ) {
channels[i].volume -= tune->channels[i].dec;
if ( channels[i].volume < 0 )
channels[i].volume = 0;
channels[i].psg->setVolume(channels[i].volume >> 2);
}
dRes = 0.0f;
}
// Mixing
right = 0;
for ( int i = 0 ; i < 8 ; i++ ) {
s = channels[i].psg->compute(1.0f/44100.0f);
if ( ! channels[i].mute )
right += s;
}
// Clipping
if ( right < -0x7FFF )
right = -0x7FFF;
else if ( right > 0x7FFF )
right = 0x7FFF;
// Filling
left = right;
*buffer++ = left;
*buffer++ = right;
dRes += 1.0f/44100.0f;
}
}
void Synth::play()
{
initialRow = row;
if ( stream )
Pa_StartStream(stream);
}
void Synth::pause()
{
if ( stream && Pa_IsStreamActive(stream) )
stop();
else
play();
}
void Synth::stop(bool abort)
{
if ( stream ) {
if ( abort )
Pa_AbortStream(stream);
else
Pa_StopStream(stream);
}
reset();
row -= (unsigned int)(latency / 60.0f * tune->rpm);
if ( row < initialRow )
row = initialRow;
}
void Synth::setRow(int l)
{
bool p = stream && Pa_IsStreamActive(stream);
if ( p )
stop();
row = l;
if ( p )
play();
}
void Synth::setChannelDuty(int ch, int duty)
{
channels[ch].psg->setDuty(duty);
}
unsigned int Synth::getCurrentRow()
{
if ( stream && Pa_IsStreamActive(stream) ) {
int r = (int)(row - latency / 60.0f * tune->rpm);
if ( r < (int) initialRow )
return initialRow;
return r;
}
return row;
}
void Synth::reset()
{
dRes = dRow = 0.0f;
for ( int i = 0 ; i < 8 ; i++ ) {
channels[i].psg->setVolume(0);
channels[i].psg->setDuty(tune->channels[i].duty);
channels[i].psg->reset();
channels[i].volume = 0;
}
}
unsigned int Synth::getVolume(int ch)
{
if ( channels[ch].mute )
return 0;
signed short s = channels[ch].psg->getSample();
if ( s < 0 )
return -s;
else
return s;
}
bool Synth::waveOut(const char * filename)
{
FILE * f = fopen(filename, "wb");
if ( f == 0 )
return false;
// RIFF header
fwrite("RIFF", 1, 4, f);
fseek(f, 4, SEEK_CUR); // will write file size after writing data :)
fwrite("WAVE", 1, 4, f);
// FMT header
fwrite("fmt ", 1, 4, f);
static const int subChunk1Size = 16;
fwrite(& subChunk1Size, 4, 1, f);
static const short int format = 1; // PCM
fwrite(& format, 2, 1, f);
static const short int nbChannels = 2;
fwrite(& nbChannels, 2, 1, f);
static const int sampleRate = 44100;
fwrite(& sampleRate, 4, 1, f);
static const int byteRate = sampleRate * nbChannels * sizeof(short int);
fwrite(& byteRate, 4, 1, f);
static const short int blockAlign = nbChannels * sizeof(short int);
fwrite(& blockAlign, 2, 1, f);
static const short int bitsPerSample = 16;
fwrite(& bitsPerSample, 2, 1, f);
stop();
int savedRow = row;
row = 0;
// DATA
fwrite("data", 1, 4, f);
fseek(f, 4, SEEK_CUR); // will write data size after writing data :)
int dataSize = 0;
bool finished = false;
static signed short buffer[2048];
while ( ! finished ) {
synth(buffer, 2048, false);
fwrite(buffer, 2, 2048 * nbChannels, f);
dataSize += 2048 * nbChannels * sizeof(short);
int end = 0;
while ( end < 8 &&
(row > tune->channels[end].lastRow && getVolume(end) == 0) )
end++;
if ( end == 8 )
finished = true;
}
// Data size
fseek(f, 40, SEEK_SET);
fwrite(& dataSize, 4, 1, f);
// File size
dataSize += 36;
fseek(f, 4, SEEK_SET);
fwrite(& dataSize, 4, 1, f);
fclose(f);
row = savedRow;
return true;
}

View File

@@ -0,0 +1,60 @@
#ifndef _SYNTH_H_
#define _SYNTH_H_
#include <portaudio.h>
#include "tune.h"
#include "psg.h"
class Synth {
PaStream * stream;
Tune * tune;
struct Channel {
PSG * psg;
int volume;
bool mute;
};
Channel channels[8];
float dRes, dRow;
unsigned int row, initialRow;
PaTime latency;
void readRow();
public:
static const int analyseBufferSize = 4096;
private:
int analyseBufferPosition;
signed short analyseBuffer[analyseBufferSize];
public:
Synth(Tune * tune);
~Synth();
void play();
void stop(bool abort = true);
void pause();
bool toggleMute(int channel)
{ return channels[channel].mute = ! channels[channel].mute; }
void setRow(int l);
void setChannelDuty(int channel, int duty);
unsigned int getCurrentRow();
unsigned int getVolume(int channel);
void synth(signed short * samples, int count, bool analyse = true);
signed short * getAnalyseBuffer() { return analyseBuffer; }
void reset();
Tune * getTune() { return tune; }
bool waveOut(const char * filename);
};
#endif // _SYNTH_H_

View File

@@ -0,0 +1,11 @@
#include "trig.h"
//int uber_sin(int x)
//{
// int ax;
// x = 4096 - (x & 0x1FFF);
// ax = x;
// if ( ax < 0 )
// ax = -ax;
// return 4 * x - ((4 * x * ax) >> 12);
//}

View File

@@ -0,0 +1,25 @@
#ifndef _TRIG_H_
#define _TRIG_H_
#ifdef __cplusplus
extern "C" {
#endif
// approximation of sin with pi=4096
//
// pi-(x mod 2pi) then
// 4.x - 4.x.|x|
int uber_sin(int x);
#define uber_cos(x) uber_sin(x + 0x800)
// Conversion from old cos/sin to new uber cos/sin
// The old was working with real value of pi in fixed point 12 (pi = 12867)
// while the new one uses pi = 4096
#define sin(x) uber_sin((((x) * 325) >> 10))
#define cos(x) uber_cos((((x) * 325) >> 10))
#ifdef __cplusplus
}
#endif
#endif // _TRIG_H_

View File

@@ -0,0 +1,249 @@
#include "tune.h"
#include "midi.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
struct NDSChannel {
unsigned int timer;
unsigned int volume;
unsigned int duty;
unsigned int dec;
int notes_offset;
};
#ifdef _WINDOWS
#pragma pack(push, 1)
#endif
struct NDSNote {
unsigned int time : 13;
unsigned int volume : 5;
unsigned int frequency : 14;
#ifdef _WINDOWS
};
#else
} __attribute__((packed));
#endif
#ifdef _WINDOWS
#pragma pack(pop)
#endif
void writeNote(int time, int volume, int frequency, FILE * f)
{
NDSNote tmpNote = { time, volume, frequency };
fwrite(& tmpNote, sizeof(NDSNote), 1, f);
#ifdef _DEBUG
printf("N time(%4d) volume(%2d) frequency(%8d)\n", time, volume, frequency);
#endif
}
int iceilf(float f)
{
return (int)f + 1;
}
bool Tune::Export(const char * filename)
{
assert(sizeof(NDSChannel) == 20);
NDSChannel tmpChannel;
int nbNotes[8];
int notes_offset = (sizeof(NDSChannel) * 8)/4;
FILE * file = fopen(filename, "wb");
if ( file == 0 )
return false;
memset(nbNotes, 0, sizeof(int) * 8);
// Jump over channels header, will be written after
fseek(file, sizeof(NDSChannel) * 8, SEEK_SET);
#ifdef _DEBUG
int dummies = 0;
#endif
int framesPerRow = iceilf(1.0f/((float)rpm/60.0f/(float)resolution));
// Write notes
for ( int i = 0 ; i < 8 ; i++ ) {
int rows = 0;
for ( int j = 0 ; j < MAXROWS ; j++ ) {
if ( channels[i].notes[j].active ) {
int time = rows * framesPerRow;
while ( time > 8191 ) { // output dummy notes to avoid overflows
writeNote(8191, 0, 1, file);
time -= 8191;
nbNotes[i]++;
#ifdef _DEBUG
dummies++;
#endif
}
writeNote(time, channels[i].notes[j].volume,
midiNoteToFrequency(channels[i].notes[j].note), file);
nbNotes[i]++;
rows = 0;
}
rows++;
}
// End of track note
writeNote(0, 0, 0, file);
nbNotes[i]++;
}
#ifdef _DEBUG
printf("Exporting '%s' created %d dummy notes.\n", filename, dummies);
#endif
fseek(file, 0, SEEK_SET);
// Write channels (header)
for ( int i = 0 ; i < 8 ; i++ ) {
tmpChannel.timer = 0;
tmpChannel.volume = 0;
tmpChannel.duty = channels[i].duty<<24;
tmpChannel.dec = channels[i].dec;
tmpChannel.notes_offset = notes_offset;
fwrite(& tmpChannel, sizeof(NDSChannel), 1, file);
notes_offset += nbNotes[i];
}
fclose(file);
return true;
}
bool Tune::Import(const char * filename)
{
// XXX //
return false;
// XXX //
NDSChannel tmpChannel;
NDSNote tmpNote;
FILE * file = fopen(filename, "rb");
if ( file == 0 )
return false;
fread(& resolution, 2, 1, file);
fread(& rpm, 2, 1, file);
// Reset everything and read channels
for ( int i = 0 ; i < 8 ; i++ ) {
channels[i].reset();
fread(& tmpChannel, sizeof(NDSChannel), 1, file);
channels[i].duty = tmpChannel.duty;
channels[i].dec = tmpChannel.dec;
}
// Read notes
for ( int i = 0 ; i < 8 ; i++ ) {
int row = 0;
do {
fread(& tmpNote, sizeof(NDSNote), 1, file);
if ( tmpNote.time != 0 ) {
row += (int)((float)(tmpNote.time - 1) / (float)resolution
/ 60.0f * (float)rpm);
if ( tmpNote.frequency != 0 )
channels[i].notes[row].active = true;
channels[i].notes[row].volume = tmpNote.volume;
channels[i].notes[row].note
= midiFrequencyToNote(tmpNote.frequency);
channels[i].lastRow = row;
row++;
}
} while ( tmpNote.time != 0 );
}
return true;
}
bool Tune::Save(const char * filename)
{
char _filename[256];
strcpy(_filename, filename);
strcat(_filename, ".txt");
FILE * f = fopen(_filename, "wb");
if ( f == 0 ) {
return false;
}
fprintf(f, "%d\n", rpm);
for ( int i = 0 ; i < 8 ; i++ )
fprintf(f, "%d %d\n", channels[i].duty, channels[i].dec);
for ( int i = 0 ; i < 8 ; i++ ) {
for ( int j = 0 ; j < MAXROWS ; j++ ) {
if ( channels[i].notes[j].active ) {
fprintf(f, "%d %d %d %d\n", i, j,
channels[i].notes[j].volume,
channels[i].notes[j].note);
}
}
}
fclose(f);
return true;
}
bool Tune::Load(const char * filename)
{
char _filename[256];
strcpy(_filename, filename);
strcat(_filename, ".txt");
FILE * f = fopen(_filename, "rb");
clear();
if ( f == 0 ) {
return false;
}
if ( fscanf(f, "%d\n", &rpm) != 1 )
goto error;
int framesPerRow = iceilf(1.0f/((float)rpm/60.0f/(float)resolution));
rpm = framesPerRow * 60;
for ( int i = 0 ; i < 8 ; i++ )
if ( fscanf(f, "%d %d\n", &(channels[i].duty), &(channels[i].dec)) != 2 )
goto error;
int c, r, v, n;
while ( fscanf(f, "%d %d %d %d\n", &c, &r, &v, &n) == 4 ) {
channels[c].notes[r].volume = v;
channels[c].notes[r].note = n;
channels[c].notes[r].active = true;
if ( channels[c].lastRow < r )
channels[c].lastRow = r;
}
fclose(f);
return true;
error:
fclose(f);
return false;
}
void Tune::clear()
{
resolution = 60;
rpm = 280;
for ( int i = 0 ; i < 8 ; i++ )
channels[i].reset();
}

View File

@@ -0,0 +1,25 @@
#ifndef _TUNE_H_
#define _TUNE_H_
#include "channel.h"
struct Tune {
Channel channels[8];
int resolution;
int rpm; // rows per minute
Tune() { clear(); }
// To text format used by the tracker
bool Save(const char * filename);
bool Load(const char * filename);
// To binary format used by the NDS player
// XXX FUCKED UP ! XXX
bool Export(const char * filename);
bool Import(const char * filename);
void clear();
};
#endif // _TUNE_H_