commit ea6f1ef80ae02c1c61d9818fcdc8974babacfb32 Author: Daniel Borges Date: Fri Nov 16 09:18:57 2012 +0100 import diff --git a/Data/vsti/IMG/vsti_troll.jpg b/Data/vsti/IMG/vsti_troll.jpg new file mode 100644 index 0000000..d1ef2f7 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll.jpg differ diff --git a/Data/vsti/IMG/vsti_troll.psd b/Data/vsti/IMG/vsti_troll.psd new file mode 100644 index 0000000..b5d32bd Binary files /dev/null and b/Data/vsti/IMG/vsti_troll.psd differ diff --git a/Data/vsti/IMG/vsti_troll_about_01.png b/Data/vsti/IMG/vsti_troll_about_01.png new file mode 100644 index 0000000..792bc91 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_about_01.png differ diff --git a/Data/vsti/IMG/vsti_troll_about_02.png b/Data/vsti/IMG/vsti_troll_about_02.png new file mode 100644 index 0000000..211f646 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_about_02.png differ diff --git a/Data/vsti/IMG/vsti_troll_about_03.png b/Data/vsti/IMG/vsti_troll_about_03.png new file mode 100644 index 0000000..bdc02b0 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_about_03.png differ diff --git a/Data/vsti/IMG/vsti_troll_ecran_grand.png b/Data/vsti/IMG/vsti_troll_ecran_grand.png new file mode 100644 index 0000000..fae567a Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_ecran_grand.png differ diff --git a/Data/vsti/IMG/vsti_troll_ecran_petit.png b/Data/vsti/IMG/vsti_troll_ecran_petit.png new file mode 100644 index 0000000..efacaab Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_ecran_petit.png differ diff --git a/Data/vsti/IMG/vsti_troll_export_01.png b/Data/vsti/IMG/vsti_troll_export_01.png new file mode 100644 index 0000000..ca1ec15 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_export_01.png differ diff --git a/Data/vsti/IMG/vsti_troll_export_02.png b/Data/vsti/IMG/vsti_troll_export_02.png new file mode 100644 index 0000000..6792d26 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_export_02.png differ diff --git a/Data/vsti/IMG/vsti_troll_export_03.png b/Data/vsti/IMG/vsti_troll_export_03.png new file mode 100644 index 0000000..7cf5b40 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_export_03.png differ diff --git a/Data/vsti/IMG/vsti_troll_fond.tga b/Data/vsti/IMG/vsti_troll_fond.tga new file mode 100644 index 0000000..1d411f5 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_fond.tga differ diff --git a/Data/vsti/IMG/vsti_troll_import_01.png b/Data/vsti/IMG/vsti_troll_import_01.png new file mode 100644 index 0000000..c26c110 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_import_01.png differ diff --git a/Data/vsti/IMG/vsti_troll_import_02.png b/Data/vsti/IMG/vsti_troll_import_02.png new file mode 100644 index 0000000..f3655af Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_import_02.png differ diff --git a/Data/vsti/IMG/vsti_troll_import_03.png b/Data/vsti/IMG/vsti_troll_import_03.png new file mode 100644 index 0000000..4051172 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_import_03.png differ diff --git a/Data/vsti/IMG/vsti_troll_instru_milieu.png b/Data/vsti/IMG/vsti_troll_instru_milieu.png new file mode 100644 index 0000000..a36fdaa Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_instru_milieu.png differ diff --git a/Data/vsti/IMG/vsti_troll_instru_moins.png b/Data/vsti/IMG/vsti_troll_instru_moins.png new file mode 100644 index 0000000..0905fb3 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_instru_moins.png differ diff --git a/Data/vsti/IMG/vsti_troll_instru_plus.png b/Data/vsti/IMG/vsti_troll_instru_plus.png new file mode 100644 index 0000000..9b37bdb Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_instru_plus.png differ diff --git a/Data/vsti/IMG/vsti_troll_potar_big_01.png b/Data/vsti/IMG/vsti_troll_potar_big_01.png new file mode 100644 index 0000000..3b95019 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_potar_big_01.png differ diff --git a/Data/vsti/IMG/vsti_troll_potar_big_02.png b/Data/vsti/IMG/vsti_troll_potar_big_02.png new file mode 100644 index 0000000..2a042d6 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_potar_big_02.png differ diff --git a/Data/vsti/IMG/vsti_troll_potar_medium_01.png b/Data/vsti/IMG/vsti_troll_potar_medium_01.png new file mode 100644 index 0000000..35dde22 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_potar_medium_01.png differ diff --git a/Data/vsti/IMG/vsti_troll_potar_medium_02.png b/Data/vsti/IMG/vsti_troll_potar_medium_02.png new file mode 100644 index 0000000..173fa42 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_potar_medium_02.png differ diff --git a/Data/vsti/IMG/vsti_troll_potar_small_01.png b/Data/vsti/IMG/vsti_troll_potar_small_01.png new file mode 100644 index 0000000..658c50c Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_potar_small_01.png differ diff --git a/Data/vsti/IMG/vsti_troll_potar_small_02.png b/Data/vsti/IMG/vsti_troll_potar_small_02.png new file mode 100644 index 0000000..a77f644 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_potar_small_02.png differ diff --git a/Data/vsti/IMG/vsti_troll_record_01.png b/Data/vsti/IMG/vsti_troll_record_01.png new file mode 100644 index 0000000..31cd6b3 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_record_01.png differ diff --git a/Data/vsti/IMG/vsti_troll_record_02.png b/Data/vsti/IMG/vsti_troll_record_02.png new file mode 100644 index 0000000..45cdfa5 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_record_02.png differ diff --git a/Data/vsti/IMG/vsti_troll_record_03.png b/Data/vsti/IMG/vsti_troll_record_03.png new file mode 100644 index 0000000..5aefb21 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_record_03.png differ diff --git a/Data/vsti/IMG/vsti_troll_slide_-1.png b/Data/vsti/IMG/vsti_troll_slide_-1.png new file mode 100644 index 0000000..7d91c7d Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_slide_-1.png differ diff --git a/Data/vsti/IMG/vsti_troll_slide_-2.png b/Data/vsti/IMG/vsti_troll_slide_-2.png new file mode 100644 index 0000000..9ba89b8 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_slide_-2.png differ diff --git a/Data/vsti/IMG/vsti_troll_slide_-3.png b/Data/vsti/IMG/vsti_troll_slide_-3.png new file mode 100644 index 0000000..82a7277 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_slide_-3.png differ diff --git a/Data/vsti/IMG/vsti_troll_slide_0.png b/Data/vsti/IMG/vsti_troll_slide_0.png new file mode 100644 index 0000000..8379502 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_slide_0.png differ diff --git a/Data/vsti/IMG/vsti_troll_slide_1.png b/Data/vsti/IMG/vsti_troll_slide_1.png new file mode 100644 index 0000000..b713927 Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_slide_1.png differ diff --git a/Data/vsti/IMG/vsti_troll_slide_2.png b/Data/vsti/IMG/vsti_troll_slide_2.png new file mode 100644 index 0000000..7e4028c Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_slide_2.png differ diff --git a/Data/vsti/IMG/vsti_troll_slide_3.png b/Data/vsti/IMG/vsti_troll_slide_3.png new file mode 100644 index 0000000..867e81b Binary files /dev/null and b/Data/vsti/IMG/vsti_troll_slide_3.png differ diff --git a/Data/vsti_troll_duty.jpg b/Data/vsti_troll_duty.jpg new file mode 100644 index 0000000..f959f41 Binary files /dev/null and b/Data/vsti_troll_duty.jpg differ diff --git a/Data/yann.tracker.vst.infos.txt b/Data/yann.tracker.vst.infos.txt new file mode 100644 index 0000000..4d138a2 --- /dev/null +++ b/Data/yann.tracker.vst.infos.txt @@ -0,0 +1,50 @@ +== Sweep +Part d'une fréquence donnée et qui s'arrête sur la note +avec comme commande : +- le premier nombre fréquence de départ (de 20 à 20000 Hz par exemple) +- et le deuxième temps du sweep (de 0 à 500 ms par exemple) + +== Vibrato +- premier nombre = amplitude de la modulation à +/- 2 octaves (-24..+24) +- deuxième nombre = fréquence de la modulation de 0.1Hz à 100Hz + (un multiple de la fréquence de la note originale, + et en plus si possible la moitié de cette fréquence) + +== Chord +une commande chord +chacun des nombres = nb de demi-tons à ajouter, comme dans lsdj +ou alors : premier nombre = deuxième note, et deuxième nombre = vitesse de l'arpège + -> par le tracker/séquenceur + +== Pitch Bend +modification de la fréquence entre -1 et +1 ton (-2 et +2 midi notes) +de la note actuellement jouée ! + +== Min/Max +attack(1, 255) +decay(1, 255) +sustain(0, 127) +release(1, 255) +sweeptime(1, 255) +sweepfreq(-127, 127) (en midinotes) + +== Interface +Settings + Volume(0,127) + Panning(0,128) + Sweep + Time(0,255) + Start(-127,127) +Envelope + Attack(1,255) + Decay(1,255) + Sustain(0,127) + Release(1,255) +System + File + Record + Import (instruments) + Export (instruments) + Author + Name + Style \ No newline at end of file diff --git a/ExportCheck/ExportCheck/ExportCheck.vcproj b/ExportCheck/ExportCheck/ExportCheck.vcproj new file mode 100644 index 0000000..1c3da02 --- /dev/null +++ b/ExportCheck/ExportCheck/ExportCheck.vcproj @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ExportCheck/ExportCheck/ExportCheck.vcxproj b/ExportCheck/ExportCheck/ExportCheck.vcxproj new file mode 100644 index 0000000..83bb07f --- /dev/null +++ b/ExportCheck/ExportCheck/ExportCheck.vcxproj @@ -0,0 +1,120 @@ + + + + + Debug + Win32 + + + NoGUI + Win32 + + + Release + Win32 + + + + {12105306-A616-447D-9408-E32E91449AB2} + ExportCheck + + + + Application + MultiByte + true + + + Application + MultiByte + true + + + Application + MultiByte + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + AllRules.ruleset + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + Disabled + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + Level3 + EditAndContinue + + + true + MachineX86 + + + + + MaxSpeed + true + MultiThreadedDLL + true + Level3 + ProgramDatabase + + + true + true + true + MachineX86 + + + + + MaxSpeed + true + MultiThreadedDLL + true + Level3 + ProgramDatabase + + + true + true + true + MachineX86 + + + + + + + + + \ No newline at end of file diff --git a/ExportCheck/ExportCheck/ExportCheck.vcxproj.user b/ExportCheck/ExportCheck/ExportCheck.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/ExportCheck/ExportCheck/ExportCheck.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ExportCheck/ExportCheck/main.cpp b/ExportCheck/ExportCheck/main.cpp new file mode 100644 index 0000000..d3bba35 --- /dev/null +++ b/ExportCheck/ExportCheck/main.cpp @@ -0,0 +1,177 @@ +#pragma pack(push, 1) +struct InstrumentParameters +{ + typedef enum { + VOLUME, + PANNING, + DETUNE, + ENVELOPE_ATTACK, + ENVELOPE_DECAY, + ENVELOPE_SUSTAIN, + ENVELOPE_RELEASE, + DUTY, + PORTAMENTO_LENGTH, + ARPEGGIO, + SWEEP_LENGTH, + SWEEP_OFFSET, + MODULATION_AMPLITUDE, + MODULATION_FREQUENCY, + TREMOLO_AMPLITUDE, + TREMOLO_FREQUENCY, + LAST, + NOTEON = 29, // for export + NOTEOFF = 30, // for export + END = 31 // for export + }; + + unsigned char iVolume; + unsigned char iPanning; + signed char iDetune; + unsigned char iDuty; + + struct { + unsigned char iAttack; // 1..255 + unsigned char iDecay; // 1..255 + unsigned char iSustain; // 0..127 + unsigned char iRelease; // 1..255 + } Envelope; + + struct { + unsigned char iAmplitude; + unsigned char iFrequency; + } Tremolo; + + unsigned char iPortamentoLength; + + struct { + unsigned char iLength; + signed short iOffset; + } Sweep; + + struct { + unsigned char iAmplitude; + unsigned char iFrequency; + } Modulation; + + unsigned char iArpeggio; + unsigned char pad; + + InstrumentParameters(); +}; +#pragma pack(pop) + +InstrumentParameters::InstrumentParameters() +: iVolume(64), + iPanning(64), + iDetune(0), + iDuty(0), + iPortamentoLength(0), + iArpeggio(0) +{ + Envelope.iAttack = 2; + Envelope.iDecay = 16; + Envelope.iSustain = 64; + Envelope.iRelease = 5; + Tremolo.iAmplitude = 0; + Tremolo.iFrequency = 0; + Sweep.iOffset = 0; + Sweep.iLength = 0; + Modulation.iAmplitude = 0; + Modulation.iFrequency = 0; +} + +#pragma pack(push, 1) +struct Command +{ + unsigned int iFrame : 24; + unsigned int iChannel : 3; + unsigned int iCommand : 5; // 0-14 = parameters, 29 = note on, 30 = note off, 31 = song end + int iValue; +}; +#pragma pack(pop) + +#include +#include +#include + +const char * GetCommandName(int c) +{ + static const char * pParameterName[InstrumentParameters::LAST] = { "vol", "pan", "dtune", "attck", "decay", "sustn", "rlz", "duty", "porta", "arp", "swlen", "swofs", "mdamp", "mdfrq", "tmamp", "tmfrq" }; + if ( c < InstrumentParameters::LAST ) + return pParameterName[c]; + if ( c == InstrumentParameters::NOTEOFF ) + return "note off"; + if ( c == InstrumentParameters::NOTEON ) + return "note on"; + if ( c == InstrumentParameters::END ) + return "end"; + return "WTF?"; +} + +int main(int argc, char ** argv) +{ + if ( argc == 2 ) + { + FILE * f = fopen(argv[1], "rb"); + if ( f ) + { + fseek(f, 0, SEEK_END); + int size = ftell(f); + fseek(f, 0, SEEK_SET); + + unsigned char * buf = new unsigned char[size]; + + fread(buf, 1, size, f); + + char txtpath[MAX_PATH]; + strcpy(txtpath, argv[1]); + strcat(txtpath, ".txt"); + FILE * txt = fopen(txtpath, "w"); + + InstrumentParameters * pParameters = (InstrumentParameters *) buf; + Command * pCommands = (Command *) (buf+sizeof(InstrumentParameters)*8); + + for ( int i = 0 ; i < 8 ; i++ ) + { + fprintf(txt, "Instrument %d\n", i); + fprintf(txt, " Volume %d\n", pParameters[i].iVolume); + fprintf(txt, " Panning %d\n", pParameters[i].iPanning-64); + fprintf(txt, " Detune %d\n", pParameters[i].iDetune); + fprintf(txt, " Duty %d\n", pParameters[i].iDuty); + fprintf(txt, " Attack %d\n", pParameters[i].Envelope.iAttack); + fprintf(txt, " Decay %d\n", pParameters[i].Envelope.iDecay); + fprintf(txt, " Sustain %d\n", pParameters[i].Envelope.iSustain); + fprintf(txt, " Release %d\n", pParameters[i].Envelope.iRelease); + fprintf(txt, " Tremolo amp %d\n", pParameters[i].Tremolo.iAmplitude); + fprintf(txt, " Tremolo frq %d\n", pParameters[i].Tremolo.iFrequency); + fprintf(txt, " Sweep len %d\n", pParameters[i].Sweep.iLength); + fprintf(txt, " Sweep ofs %3.2f\n", pParameters[i].Sweep.iOffset / 16.0f); + fprintf(txt, " Modulation amp %d\n", pParameters[i].Modulation.iAmplitude); + fprintf(txt, " Modulation frq %d\n", pParameters[i].Modulation.iFrequency); + fprintf(txt, " Portamento %d\n", pParameters[i].iPortamentoLength); + fprintf(txt, " Arpeggio %d\n", pParameters[i].iArpeggio); + } + + fputs("\n", txt); + fputs("Commands\n", txt); + fputs(" [delta t] cha command value\n\n", txt); + + while ( pCommands->iCommand != InstrumentParameters::END ) + { + fprintf(txt, " [%7d] %3d %10s ", pCommands->iFrame, pCommands->iChannel, GetCommandName(pCommands->iCommand)); + if ( pCommands->iCommand < InstrumentParameters::LAST ) + fprintf(txt, "%10d\n", pCommands->iValue); + else if ( pCommands->iCommand == InstrumentParameters::NOTEON ) //(iNote * 16 /*+ pEvent->detune*/) | (iVelocity << 16); + fprintf(txt, "%3.2f %d\n", (pCommands->iValue & 0xFFFF) / 16.0f, (pCommands->iValue >> 16) & 0x7F); + else + fprintf(txt, "\n"); + pCommands++; + } + + delete[] buf; + fclose(f); + } + } + + return 0; +} \ No newline at end of file diff --git a/NitroSynth.sln b/NitroSynth.sln new file mode 100644 index 0000000..3db8d8c --- /dev/null +++ b/NitroSynth.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NitroSynth", "NitroSynth\NitroSynth.vcxproj", "{2501786E-30DD-44A8-90D6-3E91417566A1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExportCheck", "ExportCheck\ExportCheck\ExportCheck.vcxproj", "{12105306-A616-447D-9408-E32E91449AB2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + NoGUI|Win32 = NoGUI|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2501786E-30DD-44A8-90D6-3E91417566A1}.Debug|Win32.ActiveCfg = Debug|Win32 + {2501786E-30DD-44A8-90D6-3E91417566A1}.Debug|Win32.Build.0 = Debug|Win32 + {2501786E-30DD-44A8-90D6-3E91417566A1}.NoGUI|Win32.ActiveCfg = NoGUI|Win32 + {2501786E-30DD-44A8-90D6-3E91417566A1}.NoGUI|Win32.Build.0 = NoGUI|Win32 + {2501786E-30DD-44A8-90D6-3E91417566A1}.Release|Win32.ActiveCfg = Release|Win32 + {2501786E-30DD-44A8-90D6-3E91417566A1}.Release|Win32.Build.0 = Release|Win32 + {12105306-A616-447D-9408-E32E91449AB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {12105306-A616-447D-9408-E32E91449AB2}.Debug|Win32.Build.0 = Debug|Win32 + {12105306-A616-447D-9408-E32E91449AB2}.NoGUI|Win32.ActiveCfg = NoGUI|Win32 + {12105306-A616-447D-9408-E32E91449AB2}.NoGUI|Win32.Build.0 = NoGUI|Win32 + {12105306-A616-447D-9408-E32E91449AB2}.Release|Win32.ActiveCfg = Release|Win32 + {12105306-A616-447D-9408-E32E91449AB2}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/NitroSynth/Envelope.cpp b/NitroSynth/Envelope.cpp new file mode 100644 index 0000000..0acd61c --- /dev/null +++ b/NitroSynth/Envelope.cpp @@ -0,0 +1,81 @@ +#include "Envelope.h" +#include "Instrument.h" + +#define MAX_VOLUME 512 + +Envelope::Envelope() +: iTime(0), + iLastEnvelope(0), + iVelocity(0), + iState(RELEASE) +{ +} + +unsigned int Envelope::Compute(InstrumentParameters * pParameters) +{ + unsigned int iEnvelope = 0; + + int iAttack = pParameters->Envelope.iAttack; + int iDecay = pParameters->Envelope.iDecay; + int iSustain = pParameters->Envelope.iSustain * MAX_VOLUME / 127; + int iRelease = pParameters->Envelope.iRelease; + + iTime++; + + switch ( iState ) + { + case ATTACK: + { + iEnvelope = (iTime * MAX_VOLUME) / iAttack; + if ( iTime >= iAttack ) { + iTime = 0; + iState = DECAY; + } + break; + } + + case DECAY: + { + int s = MAX_VOLUME - iSustain; + iEnvelope = iSustain + s - ((iTime * s) / iDecay); + if ( iTime >= iDecay ) { + iTime = 0; + iState = SUSTAIN; + } + break; + } + + case SUSTAIN: + { + iEnvelope = iSustain; + break; + } + + case RELEASE: + { + if ( iTime <= iRelease ) + iEnvelope = iLastEnvelope - ((iTime * iLastEnvelope) / iRelease); + break; + } + } + + if ( iState != RELEASE ) + iLastEnvelope = iEnvelope; + + iEnvelope = iEnvelope * iVelocity / 127; + + return iEnvelope / (MAX_VOLUME / 128); +} + +void Envelope::NoteOff() +{ + iTime = 0; + iState = RELEASE; +} + +void Envelope::NoteOn(int v) +{ + iVelocity = v; + iTime = 0; + iState = ATTACK; +} \ No newline at end of file diff --git a/NitroSynth/Envelope.h b/NitroSynth/Envelope.h new file mode 100644 index 0000000..efd2eeb --- /dev/null +++ b/NitroSynth/Envelope.h @@ -0,0 +1,29 @@ +#ifndef _ENVELOPE_H_ +#define _ENVELOPE_H_ + +struct InstrumentParameters; + +class Envelope +{ + typedef enum State + { + ATTACK, DECAY, SUSTAIN, RELEASE + }; + + int iTime; + int iLastEnvelope; + + int iVelocity; + + State iState; + +public: + Envelope(); + + unsigned int Compute(InstrumentParameters * pParameters); + + void NoteOn(int velocity); + void NoteOff(); +}; + +#endif // _ENVELOPE_H_ \ No newline at end of file diff --git a/NitroSynth/Export.def b/NitroSynth/Export.def new file mode 100644 index 0000000..ef7aec2 --- /dev/null +++ b/NitroSynth/Export.def @@ -0,0 +1,4 @@ +LIBRARY "NitroSynth" +EXPORTS + VSTPluginMain + main=VSTPluginMain \ No newline at end of file diff --git a/NitroSynth/Instrument.cpp b/NitroSynth/Instrument.cpp new file mode 100644 index 0000000..9627199 --- /dev/null +++ b/NitroSynth/Instrument.cpp @@ -0,0 +1,137 @@ +#include "Instrument.h" +#include "Utils.h" + +Instrument::Instrument(PSG* pSoundGenerator, InstrumentParameters* pParameters) +: pParameters(pParameters), + pSoundGenerator(pSoundGenerator), + fTime(0.0f), + iLastEnvelope(0), + iLastFrequency(0), + iLastNote(-1), + iNbFrequencies(0), + iFrequency(0), + iArpeggioTimer(0), + iEffectTimer(0), + iSweepFrequency(0), + iModulationAmplitude(0), + iPortamentoFrequency(0), + iNoteCounter(0) +{ + pFrequencies[0] = 1; +} + +Instrument::~Instrument() +{ + delete pSoundGenerator; +} + +void Instrument::Compute(float dt, int & iLeft, int & iRight) +{ + fTime += dt; + + if ( fTime > 1.0f / 60.0f ) + { + fTime -= 1.0f/60.0f; + + int iVolume = pParameters->iVolume; + if ( pParameters->Tremolo.iAmplitude != 0 ) + { + int iSin = Sinus(iEffectTimer * 12868 * pParameters->Tremolo.iFrequency / 256) / 2 + 2048; + int iAmplitude = pParameters->Tremolo.iAmplitude * pParameters->iVolume / 255; + iVolume -= iSin * iAmplitude / 4096; + } + + iLastEnvelope = oEnvelope.Compute(pParameters) * iVolume / 127; + + if ( pParameters->iArpeggio > 0 ) + { + iArpeggioTimer++; + if ( iArpeggioTimer >= pParameters->iArpeggio ) + { + iArpeggioTimer = 0; + iFrequency++; + if ( iFrequency >= iNbFrequencies ) + iFrequency = 0; + } + } + + iEffectTimer++; + + int iSweep = 0; + if ( pParameters->Sweep.iLength != 0 && iEffectTimer < pParameters->Sweep.iLength ) + iSweep = iSweepFrequency - (iEffectTimer * iSweepFrequency) / pParameters->Sweep.iLength; + + int iModulation = Sinus(iEffectTimer * 12868 * pParameters->Modulation.iFrequency / 256) * iModulationAmplitude / 4096; + + int iPortamento = 0; + if ( pParameters->iPortamentoLength != 0 && iEffectTimer < pParameters->iPortamentoLength ) + iPortamento = iPortamentoFrequency - (iEffectTimer * iPortamentoFrequency) / pParameters->iPortamentoLength; + + iLastFrequency = pFrequencies[iFrequency] + iSweep + iModulation + iPortamento; + } + + pSoundGenerator->SetDuty(pParameters->iDuty); + pSoundGenerator->SetFrequency(iLastFrequency); + + signed short iSample = pSoundGenerator->Compute(dt); + iSample = iSample * iLastEnvelope / 128; + + iRight += iSample * pParameters->iPanning / 128; + iLeft += iSample * (128 - pParameters->iPanning) / 128; +} + +void Instrument::NoteOn(int iNote, int iVelocity) +{ + iLastNote = iNote; + int n = iNote + pParameters->iDetune; + + oEnvelope.NoteOn(iVelocity); + + if ( iEffectTimer < 3 + && iNbFrequencies <= sizeof(pFrequencies)/sizeof(pFrequencies[0]) ) + { + // Arpeggio ! + pFrequencies[iNbFrequencies++] = MidiNoteToFrequency(n); + } + else + { + pFrequencies[0] = MidiNoteToFrequency(n); + iNbFrequencies = 1; + iArpeggioTimer = 0; + iEffectTimer = 0; + iFrequency = 0; + if ( iLastFrequency == 0 ) + iLastFrequency = pFrequencies[0]; + iPortamentoFrequency = iLastFrequency - pFrequencies[0]; + iSweepFrequency = MidiNoteToFrequency(n + pParameters->Sweep.iOffset) - pFrequencies[0]; + iModulationAmplitude = MidiNoteToFrequency(n + pParameters->Modulation.iAmplitude) - pFrequencies[0]; + } + iNoteCounter++; +} + +void Instrument::NoteOff(int iNote) +{ + iNoteCounter--; + if ( iNoteCounter == 0 ) + oEnvelope.NoteOff(); +} + +InstrumentParameters::InstrumentParameters() +: iVolume(64), + iPanning(64), + iDetune(0), + iDuty(0), + iPortamentoLength(0), + iArpeggio(0) +{ + Envelope.iAttack = 2; + Envelope.iDecay = 16; + Envelope.iSustain = 64; + Envelope.iRelease = 5; + Tremolo.iAmplitude = 0; + Tremolo.iFrequency = 0; + Sweep.iOffset = 0; + Sweep.iLength = 0; + Modulation.iAmplitude = 0; + Modulation.iFrequency = 0; +} \ No newline at end of file diff --git a/NitroSynth/Instrument.h b/NitroSynth/Instrument.h new file mode 100644 index 0000000..cb2ae20 --- /dev/null +++ b/NitroSynth/Instrument.h @@ -0,0 +1,103 @@ +#ifndef _INSTRUMENT_H_ +#define _INSTRUMENT_H_ + +#include "PSG.h" +#include "Envelope.h" + +#pragma pack(push, 1) +struct InstrumentParameters +{ + typedef enum { + VOLUME, + PANNING, + DETUNE, + ENVELOPE_ATTACK, + ENVELOPE_DECAY, + ENVELOPE_SUSTAIN, + ENVELOPE_RELEASE, + DUTY, + PORTAMENTO_LENGTH, + ARPEGGIO, + SWEEP_LENGTH, + SWEEP_OFFSET, + MODULATION_AMPLITUDE, + MODULATION_FREQUENCY, + TREMOLO_AMPLITUDE, + TREMOLO_FREQUENCY, + LAST, + NOTEON = 29, // for export + NOTEOFF = 30, // for export + END = 31 // for export + }; + + unsigned char iVolume; + unsigned char iPanning; + signed char iDetune; + unsigned char iDuty; + + struct { + unsigned char iAttack; // 1..255 + unsigned char iDecay; // 1..255 + unsigned char iSustain; // 0..127 + unsigned char iRelease; // 1..255 + } Envelope; + + struct { + unsigned char iAmplitude; + unsigned char iFrequency; + } Tremolo; + + unsigned char iPortamentoLength; + + struct { + unsigned char iLength; + signed short iOffset; + } Sweep; + + struct { + unsigned char iAmplitude; + unsigned char iFrequency; + } Modulation; + + unsigned char iArpeggio; + unsigned char pad; + + InstrumentParameters(); +}; +#pragma pack(pop) + +class Instrument { + InstrumentParameters* pParameters; + + PSG* pSoundGenerator; + Envelope oEnvelope; + + float fTime; + unsigned int iLastEnvelope; + int iLastFrequency; + int iLastNote; + + unsigned int pFrequencies[8]; + unsigned int iNbFrequencies; + unsigned int iFrequency; + unsigned int iArpeggioTimer; + int iEffectTimer; + int iSweepFrequency; + int iModulationAmplitude; + int iPortamentoFrequency; + + int iNoteCounter; + +public: + Instrument(PSG* pSoundGenerator, InstrumentParameters* pParameters); + ~Instrument(); + + void Compute(float dt, int & iLeft, int & iRight); + + void NoteOn(int iNote, int iVelocity); + void NoteOff(int iNote); + + void SetParameters(InstrumentParameters* p) { pParameters = p; } +}; + +#endif // _INSTRUMENT_H_ \ No newline at end of file diff --git a/NitroSynth/Main.cpp b/NitroSynth/Main.cpp new file mode 100644 index 0000000..4f3f31c --- /dev/null +++ b/NitroSynth/Main.cpp @@ -0,0 +1,6 @@ +#include "NitroSynth.h" + +AudioEffect* createEffectInstance(audioMasterCallback pCallback) +{ + return new NitroSynth(pCallback); +} \ No newline at end of file diff --git a/NitroSynth/NitroSynth.aps b/NitroSynth/NitroSynth.aps new file mode 100644 index 0000000..ad9fe42 Binary files /dev/null and b/NitroSynth/NitroSynth.aps differ diff --git a/NitroSynth/NitroSynth.cpp b/NitroSynth/NitroSynth.cpp new file mode 100644 index 0000000..7fff3fe --- /dev/null +++ b/NitroSynth/NitroSynth.cpp @@ -0,0 +1,502 @@ +#include "NitroSynth.h" +#ifndef NO_VST_GUI +#include "NitroSynthInterface.h" +#endif // NO_VST_GUI + +#include +#include + +NitroSynth::NitroSynth(audioMasterCallback pCallback) +: AudioEffectX(pCallback, 0, 128), + fSamplePeriod(1.0f/44100.0f), + bRecording(false), + iRecorderState(IDLE) +{ + if ( pCallback ) + { + setNumInputs(0); + setNumOutputs(2); + canProcessReplacing(); + isSynth(); + setUniqueID('SRTN'); + } + + for ( int i = 0 ; i < 8 ; i++ ) + if ( i < 6 ) + pInstruments[i] = new Instrument(new WaveDuty(), & pParameters[i]); + else + pInstruments[i] = new Instrument(new Noise(), & pParameters[i]); + +#ifndef NO_VST_GUI + setEditor(new NitroSynthInterface(this)); +#endif // NO_VST_GUI +} + +NitroSynth::~NitroSynth() +{ + for ( int i = 0 ; i < 8 ; i++ ) + delete pInstruments[i]; + +#ifndef NO_VST_GUI + if ( editor ) + { + delete editor; + editor = 0; + } +#endif // NO_VST_GUI +} + +void NitroSynth::processReplacing(float** pInput, float** pOutput, VstInt32 iSamples) +{ + float* pLeft = pOutput[0]; + float* pRight = pOutput[1]; + + VstTimeInfo* pTimeInfo = getTimeInfo(0); + + for ( int i = 0 ; i < iSamples ; i++ ) + { + while ( oEvents.size() != 0 && oEvents.front().iDeltaFrames == i ) + { + MidiNote oEvent = oEvents.front(); + int iNote = oEvent.iNote * 16; + + if ( oEvent.bNoteOn ) + pInstruments[oEvent.iChannel]->NoteOn(iNote, oEvent.iVelocity); + else + pInstruments[oEvent.iChannel]->NoteOff(iNote); + + if ( bRecording ) + { + double fTime = (pTimeInfo->samplePos + i) / pTimeInfo->sampleRate; + int iFrame = (int)(fTime * 1000.0 / 16.0); + + Command oCommand; + oCommand.iChannel = oEvent.iChannel; + oCommand.iFrame = iFrame; + oCommand.iCommand = oEvent.bNoteOn ? InstrumentParameters::NOTEON : InstrumentParameters::NOTEOFF; + oCommand.iValue = (iNote) | (oEvent.iVelocity << 16); + + oCommands.push_back(oCommand); + } + + oEvents.pop_front(); + } + + int iLeft = 0; + int iRight = 0; + for ( int i = 0 ; i < 8 ; i++ ) + pInstruments[i]->Compute(fSamplePeriod, iLeft, iRight); + + *pLeft++ = iLeft / (float)0x7FFF; + *pRight++ = iRight / (float)0x7FFF; + } +} + +void NitroSynth::StartRecording() +{ + bRecording = true; + oCommands.clear(); +} + +void NitroSynth::StopRecording() +{ + bRecording = false; +} + +VstInt32 NitroSynth::processEvents(VstEvents * pEvents) +{ + for ( int i = 0 ; i < pEvents->numEvents ; i++ ) + { + if ( pEvents->events[i]->type == kVstMidiType ) + { + VstMidiEvent* pEvent = (VstMidiEvent *) pEvents->events[i]; + char* pMidiData = pEvent->midiData; + + int iChannel = pMidiData[0] & 0x0F; + + if ( iChannel < 8 ) + { + int iCommand = pMidiData[0] & 0xF0; + + if ( iCommand == 0x90 || iCommand == 0x80 ) + { + MidiNote oEvent; + oEvent.iChannel = iChannel; + oEvent.iNote = (int)(pMidiData[1] & 0x7F); + oEvent.iVelocity = (int)(pMidiData[2] & 0x7F); + oEvent.bNoteOn = (iCommand == 0x90); + oEvent.iDeltaFrames = pEvent->deltaFrames; + + oEvents.push_back(oEvent); + } + } + } + } + + return 1; +} + +void NitroSynth::setSampleRate(float fSampleRate) +{ + fSamplePeriod = 1.0f / fSampleRate; +} + +bool NitroSynth::getEffectName(char * pText) +{ + strncpy(pText, "NitroSynth", kVstMaxEffectNameLen); + return true; +} + +bool NitroSynth::getProductString(char * pText) +{ + strncpy(pText, "NitroSynth", kVstMaxProductStrLen); + return true; +} + +bool NitroSynth::getVendorString(char * pText) +{ + strncpy(pText, "MsK`", kVstMaxVendorStrLen); + return true; +} + +VstInt32 NitroSynth::getVendorVersion() +{ + return 1; +} + +void NitroSynth::setParameter(VstInt32 index, float value) +{ + int iChannel = index / 16; + int iParameter = index % 16; + InstrumentParameters * pParameter = & pParameters[iChannel]; + + int iValue = -1; + + if ( iParameter < InstrumentParameters::LAST ) + { + switch ( iParameter ) + { + case InstrumentParameters::VOLUME: + iValue = pParameter->iVolume = (int) (value * 127.0f); + break; + + case InstrumentParameters::PANNING: + iValue = pParameter->iPanning = (int) (value * 128.0f); + break; + + case InstrumentParameters::DETUNE: + iValue = pParameter->iDetune = (int) ((value-0.5f) * 2.0f * 32.0f); + break; + + case InstrumentParameters::ENVELOPE_ATTACK: + if ( value <= 1.2f/127.0f ) + iValue = pParameter->Envelope.iAttack = 1; + else + iValue = pParameter->Envelope.iAttack = (int)(value * 127.0f); + break; + + case InstrumentParameters::ENVELOPE_DECAY: + if ( value <= 1.2f/127.0f ) + iValue = pParameter->Envelope.iDecay = 1; + else + iValue = pParameter->Envelope.iDecay = (int)(value * 127.0f); + break; + + case InstrumentParameters::ENVELOPE_SUSTAIN: + iValue = pParameter->Envelope.iSustain = (int)(value * 127.0f); + break; + + case InstrumentParameters::ENVELOPE_RELEASE: + if ( value <= 1.2f/127.0f ) + iValue = pParameter->Envelope.iRelease = 1; + else + iValue = pParameter->Envelope.iRelease = (int)(value * 127.0f); + break; + + case InstrumentParameters::DUTY: + iValue = pParameter->iDuty = (int)(value * 7.0f); + break; + + case InstrumentParameters::PORTAMENTO_LENGTH: + iValue = pParameter->iPortamentoLength = (int)(value * 128.0f); + break; + + case InstrumentParameters::SWEEP_LENGTH: + iValue = pParameter->Sweep.iLength = (int)(value * 127.0f); + break; + + case InstrumentParameters::SWEEP_OFFSET: + iValue = pParameter->Sweep.iOffset = (int)((value-0.5f) * 2.0f * 32.0f * 16.0f); + break; + + case InstrumentParameters::MODULATION_AMPLITUDE: + iValue = pParameter->Modulation.iAmplitude = (int)(value * 127.0f); + break; + + case InstrumentParameters::MODULATION_FREQUENCY: + iValue = pParameter->Modulation.iFrequency = (int)(value * 128.0f); + break; + + case InstrumentParameters::TREMOLO_AMPLITUDE: + iValue = pParameter->Tremolo.iAmplitude = (int)(value * 255.0f); + break; + + case InstrumentParameters::TREMOLO_FREQUENCY: + iValue = pParameter->Tremolo.iFrequency = (int)(value * 128.0f); + break; + + case InstrumentParameters::ARPEGGIO: + iValue = pParameter->iArpeggio = (int)(value * 32.0f); + break; + } + } + + if ( bRecording && iValue != -1 ) + { + VstTimeInfo* pTimeInfo = getTimeInfo(0); + if ( pTimeInfo ) + { + double iTime = pTimeInfo->samplePos / pTimeInfo->sampleRate; + int iFrame = (int)(iTime * 1000.0 / 16.0); + + Command oCommand; + oCommand.iChannel = iChannel; + oCommand.iFrame = iFrame; + oCommand.iCommand = iParameter; + oCommand.iValue = iValue; + + oCommands.push_back(oCommand); + } + } +} + +float NitroSynth::getParameter(VstInt32 index) +{ + int iChannel = index / 16; + int iParameter = index % 16; + InstrumentParameters* pParameter = & pParameters[iChannel]; + + if ( iParameter < InstrumentParameters::LAST ) + { + switch ( iParameter ) + { + case InstrumentParameters::VOLUME: + return pParameter->iVolume / 127.0f; + + case InstrumentParameters::PANNING: + return pParameter->iPanning / 128.0f; + + case InstrumentParameters::DETUNE: + return pParameter->iDetune / 32.0f / 2.0f + 0.5f; + + case InstrumentParameters::ENVELOPE_ATTACK: + return pParameter->Envelope.iAttack / 127.0f; + + case InstrumentParameters::ENVELOPE_DECAY: + return pParameter->Envelope.iDecay / 127.0f; + + case InstrumentParameters::ENVELOPE_SUSTAIN: + return pParameter->Envelope.iSustain / 127.0f; + + case InstrumentParameters::ENVELOPE_RELEASE: + return pParameter->Envelope.iRelease / 127.0f; + + case InstrumentParameters::DUTY: + return pParameter->iDuty / 7.0f; + + case InstrumentParameters::PORTAMENTO_LENGTH: + return pParameter->iPortamentoLength / 128.0f; + + case InstrumentParameters::SWEEP_LENGTH: + return pParameter->Sweep.iLength / 127.0f; + + case InstrumentParameters::SWEEP_OFFSET: + return pParameter->Sweep.iOffset / 16.0f / 32.0f / 2.0f + 0.5f; + + case InstrumentParameters::MODULATION_AMPLITUDE: + return pParameter->Modulation.iAmplitude / 127.0f; + + case InstrumentParameters::MODULATION_FREQUENCY: + return pParameter->Modulation.iFrequency / 128.0f; + + case InstrumentParameters::TREMOLO_AMPLITUDE: + return pParameter->Tremolo.iAmplitude / 255.0f; + + case InstrumentParameters::TREMOLO_FREQUENCY: + return pParameter->Tremolo.iFrequency / 128.0f; + + case InstrumentParameters::ARPEGGIO: + return pParameter->iArpeggio / 32.0f; + } + } + else if ( index == 127 ) + { + return iRecorderState / 2.0f; + } + + return 0.0f; +} + +void NitroSynth::getParameterDisplay(VstInt32 index, char* text) +{ + int iChannel = index / 16; + int iParameter = index % 16; + InstrumentParameters * pParameter = & pParameters[iChannel]; + + if ( iParameter < InstrumentParameters::LAST ) + { + switch ( iParameter ) + { + case InstrumentParameters::VOLUME: + //sprintf(text, "%d", pParameter->iVolume); + sprintf(text, "%d%%", (int)(pParameter->iVolume * (100.0/127.0))); + break; + + case InstrumentParameters::PANNING: + sprintf(text, "%d", pParameter->iPanning - 64); + break; + + case InstrumentParameters::DETUNE: + //sprintf(text, "%d", pParameter->iDetune); + sprintf(text, "%3.2f", pParameter->iDetune / 16.0f); + break; + + case InstrumentParameters::ENVELOPE_ATTACK: + sprintf(text, "%d", pParameter->Envelope.iAttack); + break; + + case InstrumentParameters::ENVELOPE_DECAY: + sprintf(text, "%d", pParameter->Envelope.iDecay); + break; + + case InstrumentParameters::ENVELOPE_SUSTAIN: + sprintf(text, "%d", pParameter->Envelope.iSustain); + break; + + case InstrumentParameters::ENVELOPE_RELEASE: + sprintf(text, "%d", pParameter->Envelope.iRelease); + break; + + case InstrumentParameters::DUTY: + //sprintf(text, "%d", pParameter->iDuty); + sprintf(text, "%d%%", (int)(pParameter->iDuty * (100.0/7.0))); + break; + + case InstrumentParameters::PORTAMENTO_LENGTH: + sprintf(text, "%d", pParameter->iPortamentoLength); + break; + + case InstrumentParameters::SWEEP_LENGTH: + sprintf(text, "%d", pParameter->Sweep.iLength); + break; + + case InstrumentParameters::SWEEP_OFFSET: + sprintf(text, "%3.2f", pParameter->Sweep.iOffset / 16.0f); + break; + + case InstrumentParameters::MODULATION_AMPLITUDE: + sprintf(text, "%d", pParameter->Modulation.iAmplitude); + break; + + case InstrumentParameters::MODULATION_FREQUENCY: + sprintf(text, "%d", pParameter->Modulation.iFrequency); + break; + + case InstrumentParameters::TREMOLO_AMPLITUDE: + sprintf(text, "%d", pParameter->Tremolo.iAmplitude); + break; + + case InstrumentParameters::TREMOLO_FREQUENCY: + sprintf(text, "%d", pParameter->Tremolo.iFrequency); + break; + + case InstrumentParameters::ARPEGGIO: + sprintf(text, "%d", pParameter->iArpeggio); + break; + } + } + else + { + strncpy(text, " ", kVstMaxParamStrLen); + } +} + +void NitroSynth::getParameterName(VstInt32 index, char* label) +{ + int iChannel = index / 16; + int iParameter = index % 16; + + if ( iParameter < InstrumentParameters::LAST ) + { + static const char * pParameterName[InstrumentParameters::LAST] = + { + "vol", "pan", "dtune", "attck", "decay", "sustn", "rlz", "duty", "porta", "arp", "swlen", "swofs", "mdamp", "mdfrq", "tmamp", "tmfrq" + }; + sprintf_s(label, kVstMaxParamStrLen, "%d-%s", iChannel, pParameterName[iParameter]); + } + else + { + strncpy(label, " ", kVstMaxParamStrLen); + } +} + +bool NitroSynth::SaveTo(const char* pFilename) const +{ + FILE* f = fopen(pFilename, "wb"); + + if ( f ) + { + for ( int i = 0 ; i < 8 ; i++ ) + fwrite(&pParameters[i], sizeof(InstrumentParameters), 1, f); + + std::vector::const_iterator it = oCommands.begin(); + int iLastFrame = 0; + + while ( it != oCommands.end() ) + { + Command oCommand = *it; + oCommand.iFrame -= iLastFrame; + fwrite(& oCommand, sizeof(Command), 1, f); + + iLastFrame = it->iFrame; + + ++it; + } + + Command oCommand; + oCommand.iChannel = 0; + oCommand.iCommand = InstrumentParameters::END; + oCommand.iFrame = 0; + oCommand.iValue = 0; + fwrite(& oCommand, sizeof(Command), 1, f); + + fclose(f); + + return true; + } + + return false; +} + +bool NitroSynth::ImportInstruments(const char* pFilename) +{ + FILE* f = fopen(pFilename, "rb"); + + if ( f ) + { + for ( int i = 0 ; i < 8 ; i++ ) + { + if ( ! fread(&pParameters[i], sizeof(InstrumentParameters), 1, f) ) + { + fclose(f); + return false; + } + } + + fclose(f); + + return true; + } + + return false; +} \ No newline at end of file diff --git a/NitroSynth/NitroSynth.h b/NitroSynth/NitroSynth.h new file mode 100644 index 0000000..742bb55 --- /dev/null +++ b/NitroSynth/NitroSynth.h @@ -0,0 +1,70 @@ +#ifndef _NITROSYNTH_H_ +#define _NITROSYNTH_H_ + +#include "audioeffectx.h" + +#include "PSG.h" +#include "Instrument.h" + +#include +#include + +#pragma pack(push, 1) +struct Command +{ + unsigned int iFrame : 24; + unsigned int iChannel : 3; + unsigned int iCommand : 5; // 0-14 = parameters, 29 = note on, 30 = note off, 31 = song end + int iValue; +}; +#pragma pack(pop) + +struct MidiNote +{ + int iChannel; + int iNote; + int iVelocity; + bool bNoteOn; + int iDeltaFrames; +}; + +class NitroSynth : public AudioEffectX +{ + float fSamplePeriod; + enum ParamRecorderState { IDLE, RECORD, SAVE } iRecorderState; + bool bRecording; + std::vector oCommands; + std::deque oEvents; + + Instrument* pInstruments[8]; + InstrumentParameters pParameters[8]; + + void processReplacing(float ** pInput, float ** pOutput, VstInt32 iSamples); + VstInt32 processEvents(VstEvents * pEvents); + void setSampleRate(float fSampleRate); + + bool getProductString(char * pText); + bool getEffectName(char * pText); + bool getVendorString(char * pText); + VstInt32 getVendorVersion(); + + void getParameterName(VstInt32 index, char* label); + +public: + NitroSynth(audioMasterCallback pCallback); + ~NitroSynth(); + + void StartRecording(); + void StopRecording(); + bool IsRecording() const { return bRecording; } + bool HasRecord() const { return oCommands.size() > 0; } + + bool SaveTo(const char* pFilename) const; + bool ImportInstruments(const char* pFilename); + + void setParameter(VstInt32 index, float value); + float getParameter(VstInt32 index); + void getParameterDisplay(VstInt32 index, char* text); +}; + +#endif // _NITROSYNTH_H_ \ No newline at end of file diff --git a/NitroSynth/NitroSynth.rc b/NitroSynth/NitroSynth.rc new file mode 100644 index 0000000..171e6ce --- /dev/null +++ b/NitroSynth/NitroSynth.rc @@ -0,0 +1,61 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE 12, 1 +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/NitroSynth/NitroSynth.vcxproj b/NitroSynth/NitroSynth.vcxproj new file mode 100644 index 0000000..7b88dad --- /dev/null +++ b/NitroSynth/NitroSynth.vcxproj @@ -0,0 +1,195 @@ + + + + + Debug + Win32 + + + NoGUI + Win32 + + + Release + Win32 + + + + {2501786E-30DD-44A8-90D6-3E91417566A1} + NitroSynth + + + + DynamicLibrary + NotSet + true + + + DynamicLibrary + NotSet + true + + + DynamicLibrary + NotSet + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + true + true + AllRules.ruleset + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + /MP %(AdditionalOptions) + Disabled + VST;VST/GUI;libpng;zlib;$(QT_HOME)\include;%(AdditionalIncludeDirectories) + _DEBUG;WIN32;_WIN32;_CRT_SECURE_NO_WARNINGS;_USRDLL;VSTXSYNTH_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebugDLL + Level3 + EditAndContinue + + + QtCored4.lib;QtGuid4.lib;%(AdditionalDependencies) + $(OutDir)$(ProjectName).dll + $(QT_HOME)\lib;%(AdditionalLibraryDirectories) + Export.def + true + Windows + false + + + MachineX86 + + + + + Full + true + Speed + VST;VST/GUI;libpng;zlib;$(QT_HOME)\include;%(AdditionalIncludeDirectories) + WIN32;_WIN32;_CRT_SECURE_NO_WARNINGS;_USRDLL;VSTXSYNTH_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + Level3 + + + + + QtCore4.lib;QtGui4.lib;%(AdditionalDependencies) + $(OutDir)$(ProjectName).dll + $(QT_HOME)\lib;%(AdditionalLibraryDirectories) + Export.def + false + Windows + true + true + false + + + MachineX86 + + + + + Full + true + Speed + VST;VST/GUI;libpng;zlib;$(QT_HOME)\include;%(AdditionalIncludeDirectories) + NO_VST_GUI;WIN32;_WIN32;_CRT_SECURE_NO_WARNINGS;_USRDLL;VSTXSYNTH_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + Level3 + + + + + %(AdditionalDependencies) + $(OutDir)$(ProjectName).dll + $(QT_HOME)\lib;%(AdditionalLibraryDirectories) + Export.def + false + Windows + true + true + false + + + MachineX86 + + + + + + + + + + + true + + + true + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NitroSynth/NitroSynthInterface.cpp b/NitroSynth/NitroSynthInterface.cpp new file mode 100644 index 0000000..8229da7 --- /dev/null +++ b/NitroSynth/NitroSynthInterface.cpp @@ -0,0 +1,119 @@ +#include "NitroSynthInterface.h" + +#include +#include +#include "NitroSynthWindow.h" + +static const char* stylesheet = +"QWidget {\ + background-color: #d9ecff;\ +}\ +\ +QLabel, QSlider {\ + background: none;\ +}\ +\ +QPushButton {\ + border: 1px solid #626a73;\ + border-radius: 7px;\ + background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #98a5b2, stop: 1 #b8c8d9);\ +}\ +\ +QPushButton:pressed {\ + background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #b8c8d9, stop: 1 #98a5b2);\ +}\ +\ +QPushButton:hover {\ + border-color: #98a5b2;\ +}\ +\ +QSlider::groove:horizontal {\ + border: 1px solid #626a73;\ + height: 8px;\ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #cee0f2, stop:1 #d9ecff);\ + margin: 2px 0;\ + border-radius: 5px;\ +}\ +\ +QSlider::handle:horizontal {\ + border: 1px solid #626a73;\ + background: qlineargradient(x1:0, y1:1, x2:0, y2:0, stop:0 #d9ecff, stop:1 #98a5b2);\ + width: 15px;\ + margin: -0.72px 0;\ + border-radius: 6px;\ +}\ +\ +QGroupBox {\ + background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #cee0f2, stop: 1 #d9ecff);\ + border: 1px solid #626a73;\ + border-radius: 7px;\ + margin-top: 2ex;\ +}\ +\ +QGroupBox::title {\ + subcontrol-origin: margin;\ + subcontrol-position: top center;\ + padding: 0 5px;\ +}\ +\ +QTabWidget::pane {\ + border-top: 1px solid #626a73;\ + position: absolute;\ + top: -0.78em;\ + padding-top: 0.78em;\ +}\ +\ +QTabWidget::tab-bar {\ + alignment: center;\ +}\ +\ +QTabBar::tab {\ + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #b8c8d9, stop: 1.0 #98a5b2);\ + border: 1px solid #626a73;\ + border-radius: 5px;\ + min-width: 8ex;\ + padding: 4px;\ +}\ +\ +QTabBar::tab:selected, QTabBar::tab:hover {\ + background: #d9ecff;\ +}\ +\ +QTabBar::tab:selected {\ + border-color: #98a5b2;\ +}"; + +NitroSynthInterface::NitroSynthInterface(AudioEffectX* pEffect) +: AEffEditor(pEffect), + pApplication(0), + pWindow(0) +{ + int argc = 0; + pApplication = new QApplication(argc, 0, true); + pApplication->setStyleSheet(stylesheet); +} + +NitroSynthInterface::~NitroSynthInterface() +{ + delete pApplication; +} + +bool NitroSynthInterface::open(void* window) +{ + pWindow = new NitroSynthWindow((NitroSynth*) effect, window); + pWindow->show(); + systemWindow = window; + return true; +} + +void NitroSynthInterface::close() +{ + pWindow->hide(); + delete pWindow; + systemWindow = 0; +} + +void NitroSynthInterface::idle() +{ + pApplication->processEvents(); +} \ No newline at end of file diff --git a/NitroSynth/NitroSynthInterface.h b/NitroSynth/NitroSynthInterface.h new file mode 100644 index 0000000..39aa41e --- /dev/null +++ b/NitroSynth/NitroSynthInterface.h @@ -0,0 +1,23 @@ +#ifndef _NITROSYNTHINTERFACE_H_ +#define _NITROSYNTHINTERFACE_H_ + +#include "aeffeditor.h" + +class QApplication; +class NitroSynthWindow; + +class NitroSynthInterface : public AEffEditor +{ + QApplication* pApplication; + NitroSynthWindow* pWindow; + + bool open(void* ptr); + void close(); + void idle(); + +public: + NitroSynthInterface(AudioEffectX* pEffect); + ~NitroSynthInterface(); +}; + +#endif // _NITROSYNTHINTERFACE_H_ \ No newline at end of file diff --git a/NitroSynth/NitroSynthWindow.cpp b/NitroSynth/NitroSynthWindow.cpp new file mode 100644 index 0000000..3991872 --- /dev/null +++ b/NitroSynth/NitroSynthWindow.cpp @@ -0,0 +1,238 @@ +#include "NitroSynthWindow.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "NitroSynth.h" + +NitroSynthWindow::NitroSynthWindow(NitroSynth* ns, void* window) +: ns(ns), + importing(false) +{ + create(WId(window)); + + setGeometry(50, 50, 480, 320); + setWindowTitle("NitroSynth"); + + pTabWidget = new QTabWidget(this); + pTabWidget->setGeometry(QRect(0, 10, 481, 274)); + + for ( int i = 0 ; i < 8 ; i++ ) + { + Tab* pTab = &pTabs[i]; + pTab->pWidget = new QWidget(pTabWidget); + + for ( int j = 0 ; j < 4 ; j++ ) + { + static const int x[] = { 13, 245, 245, 245 }; + static const int y[] = { 70, 10, 90, 170 }; + static const char* pGroupName[] = + { + "Envelope", "Sweep", "Vibrato", "Tremolo" + }; + + pTab->pGroupBoxes[j] = new QGroupBox(QString::fromAscii(pGroupName[j]), pTab->pWidget); + pTab->pGroupBoxes[j]->setGeometry(QRect(x[j], y[j], 221, j == 0 ? 111 : 71)); + pTab->pGroupBoxes[j]->raise(); + } + + for ( int j = 0 ; j < InstrumentParameters::LAST ; j++ ) + { + if ( i > 5 && j == InstrumentParameters::DUTY ) + { + pTab->pLabels[j] = 0; + pTab->pSliders[j] = 0; + pTab->pSliderValues[j] = 0; + continue; + } + + static const int x[] = + { + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 252, 252, 252, 252, 252, 252 + }; + static const int y[] = { + 10, 30, 50, 90, 110, 130, 150, 185, 205, 225, + 30, 50, 110, 130, 190, 210 + }; + static const char* pLabelName[] = + { + "Volume", "Panning", "Detune", "Attack", "Decay", "Sustain", "Release", "Pulse Width", "Portamento", "Arpeggio", + "Length", "Offset", "Amplitude", "Frequency", "Amplitude", "Frequency" + }; + + int index = i * 16 + j; + float value = ns->getParameter(index); + + pTab->pLabels[j] = new QLabel(QString::fromAscii(pLabelName[j]), pTab->pWidget); + pTab->pLabels[j]->setGeometry(QRect(x[j], y[j], 56, 20)); + pTab->pLabels[j]->raise(); + + pTab->pSliders[j] = new QSlider(pTab->pWidget); + pTab->pSliders[j]->setGeometry(QRect(x[j] + 60, y[j], 111, 19)); + pTab->pSliders[j]->setOrientation(Qt::Horizontal); + pTab->pSliders[j]->setRange(0, 127); + pTab->pSliders[j]->setValue(value * 127); + pTab->pSliders[j]->raise(); + connect(pTab->pSliders[j], SIGNAL(valueChanged(int)), this, SLOT(slider(int))); + + char txt[32]; + ns->getParameterDisplay(index, txt); + pTab->pSliderValues[j] = new QLabel(txt, pTab->pWidget); + pTab->pSliderValues[j]->setGeometry(QRect(x[j] + 177, y[j], 35, 20)); + pTab->pSliderValues[j]->raise(); + } + + static const char* pTabName[] = + { + "1 Pulse", "2 Pulse", "3 Pulse", "4 Pulse", "5 Pulse", "6 Pulse", "7 Noise", "8 Noise" + }; + pTabWidget->addTab(pTab->pWidget, QString::fromAscii(pTabName[i])); + } + + pImportButton = new QPushButton(QString::fromUtf8("Import"), this); + pImportButton->setGeometry(QRect(210, 290, 75, 23)); + pRecordButton = new QPushButton(QString::fromUtf8("Record"), this); + pRecordButton->setGeometry(QRect(300, 290, 75, 23)); + pSaveButton = new QPushButton(QString::fromUtf8("Save"), this); + pSaveButton->setGeometry(QRect(390, 290, 75, 23)); + pSaveButton->setEnabled(false); + + connect(pRecordButton, SIGNAL(clicked()), this, SLOT(recordButton())); + connect(pSaveButton, SIGNAL(clicked()), this, SLOT(saveButton())); + connect(pImportButton, SIGNAL(clicked()), this, SLOT(importButton())); +} + +NitroSynthWindow::~NitroSynthWindow() +{ + for ( int j = 0 ; j < 8 ; j++ ) + { + Tab* pTab = &pTabs[j]; + + for ( int i = 0 ; i < 14 ; i++ ) + { + if ( pTab->pLabels[i] ) delete pTab->pLabels[i]; + if ( pTab->pSliders[i] ) delete pTab->pSliders[i]; + if ( pTab->pSliderValues[i] ) delete pTab->pSliderValues[i]; + } + + for ( int i = 0 ; i < 4 ; i++ ) + { + delete pTab->pGroupBoxes[i]; + } + + delete pTab->pWidget; + } + + delete pTabWidget; + + delete pRecordButton; + delete pSaveButton; +} + +void NitroSynthWindow::recordButton() +{ + if ( ns->IsRecording() ) + { + if ( ns->HasRecord() ) + pSaveButton->setEnabled(true); + ns->StopRecording(); + pRecordButton->setText("Record"); + } + else + { + ns->StartRecording(); + pRecordButton->setText("Stop"); + } +} + +void NitroSynthWindow::importButton() +{ + QString str = QFileDialog::getOpenFileName(this, "Import instruments from"); + + if ( ns->ImportInstruments(str.toAscii()) ) + { + importing = true; + // Update sliders in all instruments + for ( int i = 0 ; i < 8 ; i++ ) + { + Tab* pTab = &pTabs[i]; + + for ( int j = 0 ; j < InstrumentParameters::LAST ; j++ ) + { + if ( !(i > 5 && j == InstrumentParameters::DUTY) ) + { + int index = i * 16 + j; + float value = ns->getParameter(index); + pTab->pSliders[j]->setValue(value * 127); + + char txt[32]; + ns->getParameterDisplay(index, txt); + pTab->pSliderValues[j]->setText(txt); + } + } + } + importing = false; + } +} + +void NitroSynthWindow::saveButton() +{ + QString str = QFileDialog::getSaveFileName(this, "Save to"); + + if ( ! ns->SaveTo(str.toAscii()) ) + QMessageBox::warning(this, "Warning", QString("Could not save to \"") + str + QString("\".")); +} + +void NitroSynthWindow::slider(int value) +{ + if ( importing ) + return; + + QSlider* slider = (QSlider*) sender(); + QWidget* widget = (QWidget*) slider->parent(); + + int channel = -1; + for ( int i = 0 ; i < 8 ; i++ ) + { + if ( pTabs[i].pWidget == widget ) + { + channel = i; + break; + } + } + + if ( channel == -1 ) + { + QMessageBox::warning(this, "WTF?!", "Unknown channel"); + return; + } + + int param = -1; + for ( int i = 0 ; i < InstrumentParameters::LAST ; i++ ) + { + if ( pTabs[channel].pSliders[i] == slider ) + { + param = i; + break; + } + } + + if ( param == -1 ) + { + QMessageBox::warning(this, "WTF?!", "Unknown parameter"); + return; + } + + int index = param + channel * 16; + ns->setParameter(index, value / 127.0f); + + char txt[32]; + ns->getParameterDisplay(index, txt); + pTabs[channel].pSliderValues[param]->setText(txt); +} \ No newline at end of file diff --git a/NitroSynth/NitroSynthWindow.h b/NitroSynth/NitroSynthWindow.h new file mode 100644 index 0000000..a436af8 --- /dev/null +++ b/NitroSynth/NitroSynthWindow.h @@ -0,0 +1,49 @@ +#ifndef _NITROSYNTHWINDOW_H_ +#define _NITROSYNTHWINDOW_H_ + +#include + +#include "Instrument.h" + +class QLabel; +class QSlider; +class QGroupBox; +class QTabWidget; +class QPushButton; +class NitroSynth; + +class NitroSynthWindow : public QWidget +{ + Q_OBJECT + + struct Tab + { + QWidget* pWidget; + QLabel* pLabels[InstrumentParameters::LAST]; + QLabel* pSliderValues[InstrumentParameters::LAST]; + QSlider* pSliders[InstrumentParameters::LAST]; + QGroupBox* pGroupBoxes[4]; + }; + + QTabWidget* pTabWidget; + Tab pTabs[8]; + + QPushButton* pImportButton; + QPushButton* pRecordButton; + QPushButton* pSaveButton; + + NitroSynth* ns; + bool importing; + +private slots: + void importButton(); + void recordButton(); + void saveButton(); + void slider(int); + +public: + NitroSynthWindow(NitroSynth* ns, void* window); + ~NitroSynthWindow(); +}; + +#endif // _NITROSYNTHWINDOW_H_ \ No newline at end of file diff --git a/NitroSynth/PSG.cpp b/NitroSynth/PSG.cpp new file mode 100644 index 0000000..bac333c --- /dev/null +++ b/NitroSynth/PSG.cpp @@ -0,0 +1,79 @@ +#include "PSG.h" + +PSG::PSG() +: iLastSample(0), + fTime(0.0f), + iFrequency(1), + iDuty(0) +{ +} + +signed short PSG::Compute(float dt) +{ + fTime += dt; + + if ( fTime >= 1.0f/(float)iFrequency ) { + iLastSample = Generate(); + fTime -= 1.0f/(float)iFrequency; + } + + return iLastSample; +} + +void PSG::Reset() +{ + fTime = 0.0f; + iLastSample = 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 <= iDuty ) + return -0x7FFF; + else + return 0x7FFF; +} + +void WaveDuty::Reset() +{ + x = 0; + PSG::Reset(); +} diff --git a/NitroSynth/PSG.h b/NitroSynth/PSG.h new file mode 100644 index 0000000..74355f0 --- /dev/null +++ b/NitroSynth/PSG.h @@ -0,0 +1,40 @@ +#ifndef _PSG_H_ +#define _PSG_H_ + +class PSG { + float fTime; + int iFrequency; + signed short iLastSample; + +protected: + int x; + int iDuty; + + virtual signed short Generate() = 0; + +public: + PSG(); + + void SetDuty(int d) { iDuty = d; } + void SetFrequency(int f) { iFrequency = f * 8; } + + virtual void Reset(); + + signed short Compute(float dt); +}; + +class Noise : public PSG { + signed short Generate(); + void Reset(); +public: + Noise() : PSG() { Reset(); } +}; + +class WaveDuty : public PSG { + signed short Generate(); + void Reset(); +public: + WaveDuty() : PSG() { Reset(); } +}; + +#endif // _PSG_H_ \ No newline at end of file diff --git a/NitroSynth/Utils.cpp b/NitroSynth/Utils.cpp new file mode 100644 index 0000000..dc7c8a8 --- /dev/null +++ b/NitroSynth/Utils.cpp @@ -0,0 +1,173 @@ +#include "Utils.h" + +#ifdef PRECISE +#include + +unsigned short MidiNoteToFrequency(int note) +{ + return (int) (440.0f * pow(2.0f, (float(note)/16.0f - 69.0f)/12.0f)); +} +#else +unsigned short MidiNoteToFrequency(int note) +{ + static const unsigned short pFrequencies[] = + { + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18, + 18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19, + 19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20, + 20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21, + 21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,23, + 23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24, + 24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25, + 25,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27, + 27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,29, + 29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30, + 30,30,31,31,31,31,31,31,31,31,32,32,32,32,32,32, + 32,32,32,33,33,33,33,33,33,33,33,34,34,34,34,34, + 34,34,34,35,35,35,35,35,35,35,35,36,36,36,36,36, + 36,36,36,37,37,37,37,37,37,37,38,38,38,38,38,38, + 38,39,39,39,39,39,39,39,40,40,40,40,40,40,40,41, + 41,41,41,41,41,41,42,42,42,42,42,42,43,43,43,43, + 43,43,43,44,44,44,44,44,44,45,45,45,45,45,45,46, + 46,46,46,46,46,47,47,47,47,47,47,48,48,48,48,48, + 48,49,49,49,49,49,50,50,50,50,50,50,51,51,51,51, + 51,52,52,52,52,52,53,53,53,53,53,54,54,54,54,54, + 55,55,55,55,55,56,56,56,56,56,57,57,57,57,57,58, + 58,58,58,58,59,59,59,59,59,60,60,60,60,61,61,61, + 61,61,62,62,62,62,63,63,63,63,64,64,64,64,64,65, + 65,65,65,66,66,66,66,67,67,67,67,68,68,68,68,69, + 69,69,69,70,70,70,70,71,71,71,71,72,72,72,72,73, + 73,73,73,74,74,74,75,75,75,75,76,76,76,76,77,77, + 77,78,78,78,78,79,79,79,80,80,80,80,81,81,81,82, + 82,82,83,83,83,83,84,84,84,85,85,85,86,86,86,86, + 87,87,87,88,88,88,89,89,89,90,90,90,91,91,91,92, + 92,92,93,93,93,94,94,94,95,95,95,96,96,96,97,97, + 97,98,98,99,99,99,100,100,100,101,101,101,102,102,103,103, + 103,104,104,104,105,105,106,106,106,107,107,108,108,108,109,109, + 110,110,110,111,111,112,112,112,113,113,114,114,114,115,115,116, + 116,116,117,117,118,118,119,119,119,120,120,121,121,122,122,123, + 123,123,124,124,125,125,126,126,127,127,128,128,128,129,129,130, + 130,131,131,132,132,133,133,134,134,135,135,136,136,137,137,138, + 138,139,139,140,140,141,141,142,142,143,143,144,144,145,145,146, + 146,147,147,148,148,149,150,150,151,151,152,152,153,153,154,155, + 155,156,156,157,157,158,158,159,160,160,161,161,162,163,163,164, + 164,165,166,166,167,167,168,169,169,170,170,171,172,172,173,173, + 174,175,175,176,177,177,178,179,179,180,181,181,182,183,183,184, + 184,185,186,187,187,188,189,189,190,191,191,192,193,193,194,195, + 195,196,197,198,198,199,200,201,201,202,203,203,204,205,206,206, + 207,208,209,209,210,211,212,212,213,214,215,216,216,217,218,219, + 220,220,221,222,223,224,224,225,226,227,228,228,229,230,231,232, + 233,233,234,235,236,237,238,239,239,240,241,242,243,244,245,246, + 246,247,248,249,250,251,252,253,254,255,256,256,257,258,259,260, + 261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276, + 277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292, + 293,294,295,296,297,299,300,301,302,303,304,305,306,307,308,310, + 311,312,313,314,315,316,317,319,320,321,322,323,324,326,327,328, + 329,330,332,333,334,335,336,338,339,340,341,342,344,345,346,347, + 349,350,351,353,354,355,356,358,359,360,362,363,364,366,367,368, + 369,371,372,374,375,376,378,379,380,382,383,384,386,387,389,390, + 391,393,394,396,397,399,400,402,403,404,406,407,409,410,412,413, + 415,416,418,419,421,422,424,425,427,429,430,432,433,435,436,438, + 440,441,443,444,446,448,449,451,452,454,456,457,459,461,462,464, + 466,467,469,471,472,474,476,478,479,481,483,485,486,488,490,492, + 493,495,497,499,501,502,504,506,508,510,512,513,515,517,519,521, + 523,525,527,528,530,532,534,536,538,540,542,544,546,548,550,552, + 554,556,558,560,562,564,566,568,570,572,574,576,578,581,583,585, + 587,589,591,593,595,598,600,602,604,606,608,611,613,615,617,620, + 622,624,626,629,631,633,635,638,640,642,645,647,649,652,654,656, + 659,661,664,666,668,671,673,676,678,681,683,685,688,690,693,695, + 698,700,703,706,708,711,713,716,718,721,724,726,729,732,734,737, + 739,742,745,748,750,753,756,758,761,764,767,769,772,775,778,781, + 783,786,789,792,795,798,801,804,806,809,812,815,818,821,824,827, + 830,833,836,839,842,845,848,851,854,858,861,864,867,870,873,876, + 880,883,886,889,892,896,899,902,905,909,912,915,918,922,925,928, + 932,935,939,942,945,949,952,956,959,963,966,970,973,977,980,984, + 987,991,994,998,1002,1005,1009,1013,1016,1020,1024,1027,1031,1035,1038,1042, + 1046,1050,1054,1057,1061,1065,1069,1073,1077,1081,1084,1088,1092,1096,1100,1104, + 1108,1112,1116,1120,1124,1128,1133,1137,1141,1145,1149,1153,1157,1162,1166,1170, + 1174,1178,1183,1187,1191,1196,1200,1204,1209,1213,1217,1222,1226,1231,1235,1240, + 1244,1249,1253,1258,1262,1267,1271,1276,1280,1285,1290,1294,1299,1304,1309,1313, + 1318,1323,1328,1332,1337,1342,1347,1352,1357,1362,1366,1371,1376,1381,1386,1391, + 1396,1401,1407,1412,1417,1422,1427,1432,1437,1443,1448,1453,1458,1464,1469,1474, + 1479,1485,1490,1496,1501,1506,1512,1517,1523,1528,1534,1539,1545,1551,1556,1562, + 1567,1573,1579,1585,1590,1596,1602,1608,1613,1619,1625,1631,1637,1643,1649,1655, + 1661,1667,1673,1679,1685,1691,1697,1703,1709,1716,1722,1728,1734,1741,1747,1753, + 1760,1766,1772,1779,1785,1792,1798,1805,1811,1818,1824,1831,1837,1844,1851,1857, + 1864,1871,1878,1884,1891,1898,1905,1912,1919,1926,1933,1940,1947,1954,1961,1968, + 1975,1982,1989,1997,2004,2011,2018,2026,2033,2040,2048,2055,2062,2070,2077,2085, + 2093,2100,2108,2115,2123,2131,2138,2146,2154,2162,2169,2177,2185,2193,2201,2209, + 2217,2225,2233,2241,2249,2257,2266,2274,2282,2290,2298,2307,2315,2324,2332,2340, + 2349,2357,2366,2374,2383,2392,2400,2409,2418,2426,2435,2444,2453,2462,2471,2480, + 2489,2498,2507,2516,2525,2534,2543,2552,2561,2571,2580,2589,2599,2608,2618,2627, + 2637,2646,2656,2665,2675,2685,2694,2704,2714,2724,2733,2743,2753,2763,2773,2783, + 2793,2803,2814,2824,2834,2844,2855,2865,2875,2886,2896,2907,2917,2928,2938,2949, + 2959,2970,2981,2992,3003,3013,3024,3035,3046,3057,3068,3079,3091,3102,3113,3124, + 3135,3147,3158,3170,3181,3193,3204,3216,3227,3239,3251,3263,3274,3286,3298,3310, + 3322,3334,3346,3358,3370,3382,3395,3407,3419,3432,3444,3457,3469,3482,3494,3507, + 3520,3532,3545,3558,3571,3584,3597,3610,3623,3636,3649,3662,3675,3689,3702,3715, + 3729,3742,3756,3769,3783,3797,3810,3824,3838,3852,3866,3880,3894,3908,3922,3936, + 3951,3965,3979,3994,4008,4023,4037,4052,4066,4081,4096,4111,4125,4140,4155,4170, + 4186,4201,4216,4231,4246,4262,4277,4293,4308,4324,4339,4355,4371,4387,4403,4418, + 4434,4450,4467,4483,4499,4515,4532,4548,4564,4581,4597,4614,4631,4648,4664,4681, + 4698,4715,4732,4749,4766,4784,4801,4818,4836,4853,4871,4888,4906,4924,4942,4960, + 4978,4996,5014,5032,5050,5068,5087,5105,5123,5142,5161,5179,5198,5217,5236,5255, + 5274,5293,5312,5331,5350,5370,5389,5409,5428,5448,5467,5487,5507,5527,5547,5567, + 5587,5607,5628,5648,5668,5689,5710,5730,5751,5772,5793,5814,5835,5856,5877,5898, + 5919,5941,5962,5984,6006,6027,6049,6071,6093,6115,6137,6159,6182,6204,6226,6249, + 6271,6294,6317,6340,6363,6386,6409,6432,6455,6479,6502,6526,6549,6573,6597,6620, + 6644,6668,6693,6717,6741,6765,6790,6814,6839,6864,6889,6914,6939,6964,6989,7014, + 7040,7065,7091,7116,7142,7168,7194,7220,7246,7272,7298,7325,7351,7378,7404,7431, + 7458,7485,7512,7539,7567,7594,7621,7649,7677,7704,7732,7760,7788,7817,7845,7873, + 7902,7930,7959,7988,8017,8046,8075,8104,8133,8163,8192,8222,8251,8281,8311,8341, + 8372,8402,8432,8463,8493,8524,8555,8586,8617,8648,8679,8711,8742,8774,8806,8837, + 8869,8901,8934,8966,8998,9031,9064,9096,9129,9162,9195,9229,9262,9296,9329,9363, + 9397,9431,9465,9499,9533,9568,9603,9637,9672,9707,9742,9777,9813,9848,9884,9920, + 9956,9992,10028,10064,10100,10137,10174,10210,10247,10284,10322,10359,10396,10434,10472,10510, + 10548,10586,10624,10662,10701,10740,10779,10818,10857,10896,10935,10975,11015,11054,11094,11135, + 11175,11215,11256,11296,11337,11378,11420,11461,11502,11544,11586,11628,11670,11712,11754,11797, + 11839,11882,11925,11968,12012,12055,12099,12142,12186,12230,12275,12319,12364,12408,12453,12498, + 12543,12589,12634,12680,12726,12772,12818,12864,12911,12958,13004,13052,13099,13146,13194,13241, + }; + + if ( note < 0 ) + note = 0; + if ( note > 127*16 ) + note = 127*16; + + return pFrequencies[note]; +} +#endif + +int Abs(int x) +{ + if ( x < 0 ) + return -x; + return x; +} + +int Sinus(int x) +{ + // translate x into [-pi, pi].f12 + while ( x > 12868 ) + x -= 25736; + while ( x < -12868 ) + x += 25736; + // compute sin(x) + // see http://www.devmaster.net/forums/showthread.php?t=5784 + const int B = 5215; + const int C = -1660; + return (B*x + ((C*x)>>12)*Abs(x))>>12; +} \ No newline at end of file diff --git a/NitroSynth/Utils.h b/NitroSynth/Utils.h new file mode 100644 index 0000000..3d0fca7 --- /dev/null +++ b/NitroSynth/Utils.h @@ -0,0 +1,8 @@ +#ifndef _MIDI_H_ +#define _MIDI_H_ + +unsigned short MidiNoteToFrequency(int note); +int Abs(int x); +int Sinus(int x); // fixed point .12 + +#endif // _MIDI_H_ \ No newline at end of file diff --git a/NitroSynth/VST/README b/NitroSynth/VST/README new file mode 100644 index 0000000..595ae91 --- /dev/null +++ b/NitroSynth/VST/README @@ -0,0 +1 @@ +Add here VST SDK 2.4 diff --git a/NitroSynth/resource.h b/NitroSynth/resource.h new file mode 100644 index 0000000..9247e69 --- /dev/null +++ b/NitroSynth/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by NitroSynth.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Player/Makefile b/Player/Makefile new file mode 100644 index 0000000..01758b8 --- /dev/null +++ b/Player/Makefile @@ -0,0 +1,41 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +export TARGET := $(shell basename $(CURDIR)) +export TOPDIR := $(CURDIR) + + +.PHONY: $(TARGET).arm7 $(TARGET).arm9 + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all: $(TARGET).nds + +#--------------------------------------------------------------------------------- +$(TARGET).nds : $(TARGET).arm7 $(TARGET).arm9 + ndstool -c $(TARGET).nds -7 $(TARGET).arm7 -9 $(TARGET).arm9 + +#--------------------------------------------------------------------------------- +$(TARGET).arm7 : arm7/$(TARGET).elf +$(TARGET).arm9 : arm9/$(TARGET).elf + +#--------------------------------------------------------------------------------- +arm7/$(TARGET).elf: + $(MAKE) -C arm7 + +#--------------------------------------------------------------------------------- +arm9/$(TARGET).elf: + $(MAKE) -C arm9 + +#--------------------------------------------------------------------------------- +clean: + $(MAKE) -C arm9 clean + $(MAKE) -C arm7 clean + rm -f $(TARGET).nds $(TARGET).arm7 $(TARGET).arm9 diff --git a/Player/arm7/Makefile b/Player/arm7/Makefile new file mode 100644 index 0000000..92ca8f5 --- /dev/null +++ b/Player/arm7/Makefile @@ -0,0 +1,132 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +# DATA is a list of directories containing binary files +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD := build +SOURCES := source +INCLUDES := include build +DATA := + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb-interwork -marm + +CFLAGS := -g -Wall -O2\ + -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\ + -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM7 +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + + +ASFLAGS := -g $(ARCH) -Isource +LDFLAGS = -specs=ds_arm7.specs -g $(ARCH) -Wl,-Map,$(notdir $*).map + +LIBS := -ldswifi7 -lmm7 -lnds7 + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBNDS) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export ARM7BIN := $(TOPDIR)/$(TARGET).arm7 +export ARM7ELF := $(CURDIR)/$(TARGET).arm7.elf +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) *.elf + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(ARM7BIN) : $(ARM7ELF) + @$(OBJCOPY) -O binary $< $@ + @echo built ... $(notdir $@) + + +$(ARM7ELF) : $(OFILES) + @echo linking $(notdir $@) + @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/Player/arm7/source/Envelope.c b/Player/arm7/source/Envelope.c new file mode 100644 index 0000000..18987e0 --- /dev/null +++ b/Player/arm7/source/Envelope.c @@ -0,0 +1,85 @@ +#include "Envelope.h" +#include "InstrumentParameters.h" + +#define MAX_VOLUME 512 + +void Envelope_Init(Envelope * pEnvelope) +{ + pEnvelope->iTime = 0; + pEnvelope->iLastEnvelope = 0; + pEnvelope->iVelocity = 0; + pEnvelope->iState = RELEASE; +} + +unsigned int Envelope_Compute(Envelope * pEnvelope, InstrumentParameters * pParameters) +{ + unsigned int iEnvelope = 0; + + int iAttack = pParameters->iAttack; + int iDecay = pParameters->iDecay; + int iSustain = pParameters->iSustain * MAX_VOLUME / 127; + int iRelease = pParameters->iRelease; + + pEnvelope->iTime++; + + switch ( pEnvelope->iState ) + { + case ATTACK: + { + iEnvelope = (pEnvelope->iTime * MAX_VOLUME) / iAttack; + if ( pEnvelope->iTime >= iAttack ) { + pEnvelope->iTime = 0; + pEnvelope->iState = DECAY; + } + break; + } + + case DECAY: + { + int s = MAX_VOLUME - iSustain; + iEnvelope = iSustain + s - ((pEnvelope->iTime * s) / iDecay); + if ( pEnvelope->iTime >= iDecay ) { + pEnvelope->iTime = 0; + pEnvelope->iState = SUSTAIN; + } + break; + } + + case SUSTAIN: + { + iEnvelope = iSustain; + break; + } + + case RELEASE: + { + if ( pEnvelope->iTime <= iRelease ) + iEnvelope = pEnvelope->iLastEnvelope - + ((pEnvelope->iTime * pEnvelope->iLastEnvelope) / iRelease); + break; + } + } + + if ( pEnvelope->iState != RELEASE ) + pEnvelope->iLastEnvelope = iEnvelope; + + iEnvelope = iEnvelope * pEnvelope->iVelocity / 127; + + return iEnvelope / (MAX_VOLUME / 128); +} + +void Envelope_NoteOn(Envelope * pEnvelope, int v) +{ + pEnvelope->iVelocity = v; + pEnvelope->iTime = 0; + pEnvelope->iState = ATTACK; +} + +void Envelope_NoteOff(Envelope * pEnvelope) +{ + if ( pEnvelope->iState != RELEASE ) + { + pEnvelope->iTime = 0; + pEnvelope->iState = RELEASE; + } +} diff --git a/Player/arm7/source/Envelope.h b/Player/arm7/source/Envelope.h new file mode 100644 index 0000000..a6ba241 --- /dev/null +++ b/Player/arm7/source/Envelope.h @@ -0,0 +1,31 @@ +#ifndef _ENVELOPE_H_ +#define _ENVELOPE_H_ + +#include "InstrumentParameters.h" + +typedef enum +{ + ATTACK, + DECAY, + SUSTAIN, + RELEASE +} +EnvelopeState; + +typedef struct +{ + int iTime; + int iLastEnvelope; + + int iVelocity; + + EnvelopeState iState; +} +Envelope; + +void Envelope_Init(Envelope * pEnvelope); +unsigned int Envelope_Compute(Envelope * pEnvelope, InstrumentParameters * pParameters); +void Envelope_NoteOn(Envelope * pEnvelope, int velocity); +void Envelope_NoteOff(Envelope * pEnvelope); + +#endif // _ENVELOPE_H_ diff --git a/Player/arm7/source/Instrument.c b/Player/arm7/source/Instrument.c new file mode 100644 index 0000000..fbe6452 --- /dev/null +++ b/Player/arm7/source/Instrument.c @@ -0,0 +1,109 @@ +#include "Instrument.h" +#include "Utils.h" +#include + +#define SOUND_PSG_DUTY(duty) ((duty)<<24) + +void Instrument_Init(Instrument* pInstrument, int iChannel, InstrumentParameters* pParameters) +{ + pInstrument->pParameters = pParameters; + pInstrument->iChannel = iChannel; + pInstrument->iLastFrequency = 0; + pInstrument->pFrequencies[0] = 1; + pInstrument->iFrequency = 0; + pInstrument->iNbFrequencies = 0; + pInstrument->iEffectTimer = 0; + pInstrument->iSweepFrequency = 0; + pInstrument->iModulationAmplitude = 0; + pInstrument->iPortamentoFrequency = 0; + pInstrument->iArpeggioTimer = 0; + + Envelope_Init(&pInstrument->oEnvelope); +} + +void Instrument_Compute(Instrument* pInstrument) +{ + InstrumentParameters* pParameters = pInstrument->pParameters; + + int iVolume = pParameters->iVolume; + + if ( pParameters->iTremoloAmplitude != 0 ) + { + int iSin = Sinus(pInstrument->iEffectTimer * 12868 * pParameters->iTremoloFrequency / 256) / 2 + 2048; + int iAmplitude = pParameters->iTremoloAmplitude * pParameters->iVolume / 255; + iVolume -= iSin * iAmplitude / 4096; + } + + iVolume = Envelope_Compute(& pInstrument->oEnvelope, pParameters) * iVolume / 127; + + if ( pParameters->iArpeggio > 0 ) + { + pInstrument->iArpeggioTimer++; + if ( pInstrument->iArpeggioTimer >= pParameters->iArpeggio ) + { + pInstrument->iArpeggioTimer = 0; + pInstrument->iFrequency++; + if ( pInstrument->iFrequency >= pInstrument->iNbFrequencies ) + pInstrument->iFrequency = 0; + } + } + + pInstrument->iEffectTimer++; + + int iSweep = 0; + if ( pParameters->iSweepLength != 0 + && pInstrument->iEffectTimer < pParameters->iSweepLength ) + iSweep = pInstrument->iSweepFrequency - (pInstrument->iEffectTimer * pInstrument->iSweepFrequency) / pParameters->iSweepLength; + + int iModulation = Sinus(pInstrument->iEffectTimer * 12868 * pParameters->iModulationFrequency / 256) * pInstrument->iModulationAmplitude / 4096; + + int iPortamento = 0; + if ( pParameters->iPortamentoLength != 0 && pInstrument->iEffectTimer < pParameters->iPortamentoLength ) + iPortamento = pInstrument->iPortamentoFrequency - (pInstrument->iEffectTimer * pInstrument->iPortamentoFrequency) / pParameters->iPortamentoLength; + + pInstrument->iLastFrequency = pInstrument->pFrequencies[pInstrument->iFrequency] + iSweep + iModulation + iPortamento; + + if ( pInstrument->iLastFrequency <= 0 ) + pInstrument->iLastFrequency = 1; + + SCHANNEL_CR(pInstrument->iChannel) = SCHANNEL_ENABLE + | SOUND_VOL(iVolume) + | SOUND_PAN(pParameters->iPanning) + | SOUND_FORMAT_PSG + | SOUND_PSG_DUTY(pParameters->iDuty) + ; + SCHANNEL_TIMER(pInstrument->iChannel) = SOUND_FREQ(pInstrument->iLastFrequency * 8); +} + +void Instrument_NoteOn(Instrument* pInstrument, int iNote, int iVelocity) +{ + int n = iNote + pInstrument->pParameters->iDetune; + + Envelope_NoteOn(& pInstrument->oEnvelope, iVelocity); + + if ( pInstrument->iEffectTimer < 3 + && pInstrument->iNbFrequencies <= sizeof(pInstrument->pFrequencies)/sizeof(pInstrument->pFrequencies[0]) ) + { + // Arpeggio ! + pInstrument->pFrequencies[pInstrument->iNbFrequencies++] = MidiNoteToFrequency(n); + } + else + { + pInstrument->pFrequencies[0] = MidiNoteToFrequency(n); + pInstrument->iNbFrequencies = 1; + pInstrument->iArpeggioTimer = 0; + pInstrument->iEffectTimer = 0; + pInstrument->iFrequency = 0; + if ( pInstrument->iLastFrequency == 0 ) + pInstrument->iLastFrequency = pInstrument->pFrequencies[0]; + pInstrument->iPortamentoFrequency = pInstrument->iLastFrequency - pInstrument->pFrequencies[0]; + pInstrument->iSweepFrequency = MidiNoteToFrequency(n + pInstrument->pParameters->iSweepOffset) - pInstrument->pFrequencies[0]; + pInstrument->iModulationAmplitude = MidiNoteToFrequency(n + pInstrument->pParameters->iModulationAmplitude) - pInstrument->pFrequencies[0]; + } +} + +void Instrument_NoteOff(Instrument* pInstrument) +{ + Envelope_NoteOff(& pInstrument->oEnvelope); +} + diff --git a/Player/arm7/source/Instrument.h b/Player/arm7/source/Instrument.h new file mode 100644 index 0000000..f10d007 --- /dev/null +++ b/Player/arm7/source/Instrument.h @@ -0,0 +1,60 @@ +#ifndef _INSTRUMENT_H_ +#define _INSTRUMENT_H_ + +#include "Envelope.h" +#include "InstrumentParameters.h" + +enum { + VOLUME, + PANNING, + DETUNE, + ENVELOPE_ATTACK, + ENVELOPE_DECAY, + ENVELOPE_SUSTAIN, + ENVELOPE_RELEASE, + DUTY, + PORTAMENTO_LENGTH, + ARPEGGIO, + SWEEP_LENGTH, + SWEEP_OFFSET, + MODULATION_AMPLITUDE, + MODULATION_FREQUENCY, + TREMOLO_AMPLITUDE, + TREMOLO_FREQUENCY, + LAST, + NOTEON = 29, // for export + NOTEOFF = 30, // for export + END = 31 // for export +}; + +struct sInstrument { + InstrumentParameters * pParameters; + + int iChannel; + Envelope oEnvelope; + + float fTime; + int iLastFrequency; + + unsigned int pFrequencies[8]; + unsigned int iNbFrequencies; + unsigned int iFrequency; + unsigned int iArpeggioTimer; + int iEffectTimer; + int iSweepFrequency; + int iModulationAmplitude; + int iPortamentoFrequency; +}; + +typedef struct sInstrument Instrument; + +void Instrument_Init(Instrument * pInstrument, int iChannel, InstrumentParameters * pParameters); + +void Instrument_Compute(Instrument * pInstrument); + +void Instrument_NoteOn(Instrument * pInstrument, int iNote, int iVelocity); +void Instrument_NoteOff(Instrument * pInstrument); + +void Instrument_SetParameters(Instrument * pInstrument, InstrumentParameters * p); + +#endif // _INSTRUMENT_H_ diff --git a/Player/arm7/source/InstrumentParameters.c b/Player/arm7/source/InstrumentParameters.c new file mode 100644 index 0000000..ae05b28 --- /dev/null +++ b/Player/arm7/source/InstrumentParameters.c @@ -0,0 +1,21 @@ +#include "InstrumentParameters.h" + +void InstrumentParameters_Init(InstrumentParameters * pParameters) +{ + pParameters->iVolume = 64; + pParameters->iPanning = 64; + pParameters->iDetune = 0; + pParameters->iDuty = 0; + pParameters->iPortamentoLength = 0; + pParameters->iAttack = 2; + pParameters->iDecay = 16; + pParameters->iSustain = 64; + pParameters->iRelease = 5; + pParameters->iTremoloAmplitude = 0; + pParameters->iTremoloFrequency = 0; + pParameters->iSweepOffset = 0; + pParameters->iSweepLength = 0; + pParameters->iModulationAmplitude = 0; + pParameters->iModulationFrequency = 0; + pParameters->iArpeggio = 0; +} diff --git a/Player/arm7/source/InstrumentParameters.h b/Player/arm7/source/InstrumentParameters.h new file mode 100644 index 0000000..258d807 --- /dev/null +++ b/Player/arm7/source/InstrumentParameters.h @@ -0,0 +1,35 @@ +#ifndef _INSTRUMENTPARAMETERS_H_ +#define _INSTRUMENTPARAMETERS_H_ + +struct sInstrumentParameters +{ + unsigned char iVolume; + unsigned char iPanning; + signed char iDetune; + unsigned char iDuty; + + unsigned char iAttack; // 1..255 + unsigned char iDecay; // 1..255 + unsigned char iSustain; // 0..127 + unsigned char iRelease; // 1..255 + + unsigned char iTremoloAmplitude; + unsigned char iTremoloFrequency; + + unsigned char iPortamentoLength; + + unsigned char iSweepLength; + signed short iSweepOffset; + + unsigned char iModulationAmplitude; + unsigned char iModulationFrequency; + + unsigned char iArpeggio; + unsigned char pad; +}; + +typedef struct sInstrumentParameters InstrumentParameters; + +void InstrumentParameters_Init(InstrumentParameters * pParameters); + +#endif // _INSTRUMENTPARAMETERS_H_ diff --git a/Player/arm7/source/Player.c b/Player/arm7/source/Player.c new file mode 100644 index 0000000..c24eaa9 --- /dev/null +++ b/Player/arm7/source/Player.c @@ -0,0 +1,155 @@ +#include "Player.h" +#include "Utils.h" +#include + +void Player_Handler(u32 iCommand, void* pUserdata) +{ + Player* pPlayer = (Player *) pUserdata; + if ( iCommand == 0 ) + Player_Stop(pPlayer); + else + Player_Play(pPlayer, (unsigned char *) iCommand); +} + +void Player_Init(Player* pPlayer) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + InstrumentParameters_Init(& pPlayer->pParameters[i]); + Instrument_Init(& pPlayer->pInstruments[i], i + 8, & pPlayer->pParameters[i]); + } + + pPlayer->pCommand = 0; + pPlayer->iTimer = 0; + + fifoSetValue32Handler(FIFO_USER_01, Player_Handler, pPlayer); +} + +void Player_Play(Player* pPlayer, unsigned char* pData) +{ + MemCopy(pPlayer->pParameters, pData, sizeof(InstrumentParameters)*8); + pPlayer->pCommand = (Command *) (pData + sizeof(InstrumentParameters)*8); + pPlayer->iTimer = 0; +} + +void Player_Stop(Player* pPlayer) +{ + pPlayer->pCommand = 0; + + int i; + for ( i = 0 ; i < 8 ; i++ ) + Instrument_NoteOff(& pPlayer->pInstruments[i]); +} + +void Player_Execute(Player* pPlayer) +{ + int i; + + if ( pPlayer->pCommand != 0 ) + { + int iCommand = pPlayer->pCommand->iCommand; + + while ( iCommand != END && pPlayer->iTimer == pPlayer->pCommand->iFrame ) + { + Instrument* pInstrument = & pPlayer->pInstruments[pPlayer->pCommand->iChannel]; + InstrumentParameters* pParameters = & pPlayer->pParameters[pPlayer->pCommand->iChannel]; + int iValue = pPlayer->pCommand->iValue; + + switch ( iCommand ) + { + case VOLUME: + pParameters->iVolume = iValue; + break; + + case PANNING: + pParameters->iPanning = iValue; + break; + + case DETUNE: + pParameters->iDetune = iValue; + break; + + case ENVELOPE_ATTACK: + pParameters->iAttack = iValue; + break; + + case ENVELOPE_DECAY: + pParameters->iDecay = iValue; + break; + + case ENVELOPE_SUSTAIN: + pParameters->iSustain = iValue; + break; + + case ENVELOPE_RELEASE: + pParameters->iRelease = iValue; + break; + + case DUTY: + pParameters->iDuty = iValue; + break; + + case PORTAMENTO_LENGTH: + pParameters->iPortamentoLength = iValue; + break; + + case SWEEP_LENGTH: + pParameters->iSweepLength = iValue; + break; + + case SWEEP_OFFSET: + pParameters->iSweepOffset = iValue; + break; + + case MODULATION_AMPLITUDE: + pParameters->iModulationAmplitude = iValue; + break; + + case MODULATION_FREQUENCY: + pParameters->iModulationFrequency = iValue; + break; + + case TREMOLO_AMPLITUDE: + pParameters->iTremoloAmplitude = iValue; + break; + + case TREMOLO_FREQUENCY: + pParameters->iTremoloFrequency = iValue; + break; + + case ARPEGGIO: + pParameters->iArpeggio = iValue; + break; + + case NOTEON: + Instrument_NoteOn(pInstrument, + iValue & 0xFFFF, + iValue >> 16); + break; + + case NOTEOFF: + Instrument_NoteOff(pInstrument); + break; + } + + pPlayer->pCommand++; + pPlayer->iTimer = 0; + + iCommand = pPlayer->pCommand->iCommand; + } + + if ( pPlayer->pCommand->iCommand == END ) + { + for ( i = 0 ; i < 8 ; i++ ) + Instrument_NoteOff(& pPlayer->pInstruments[i]); + pPlayer->pCommand = 0; + } + + pPlayer->iTimer++; + } + + for ( i = 0 ; i < 8 ; i++ ) + Instrument_Compute(& pPlayer->pInstruments[i]); +} diff --git a/Player/arm7/source/Player.h b/Player/arm7/source/Player.h new file mode 100644 index 0000000..a109537 --- /dev/null +++ b/Player/arm7/source/Player.h @@ -0,0 +1,25 @@ +#ifndef _PLAYER_H_ +#define _PLAYER_H_ + +#include "Instrument.h" + +typedef struct { + unsigned int iFrame : 24; + unsigned int iChannel : 3; + unsigned int iCommand : 5; // see Instrument.h + int iValue; +} Command; + +typedef struct { + Instrument pInstruments[8]; + InstrumentParameters pParameters[8]; + Command * pCommand; + int iTimer; +} Player; + +void Player_Init(Player * pPlayer); +void Player_Play(Player * pPlayer, unsigned char * pData); +void Player_Stop(Player * pPlayer); +void Player_Execute(Player * pPlayer); + +#endif // _PLAYER_H_ diff --git a/Player/arm7/source/Utils.c b/Player/arm7/source/Utils.c new file mode 100644 index 0000000..8f0552e --- /dev/null +++ b/Player/arm7/source/Utils.c @@ -0,0 +1,174 @@ +#include "Utils.h" + +unsigned short MidiNoteToFrequency(int note) +{ + static const unsigned short pFrequencies[] = + { + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18, + 18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19, + 19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20, + 20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21, + 21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,23, + 23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24, + 24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25, + 25,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27, + 27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,29, + 29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30, + 30,30,31,31,31,31,31,31,31,31,32,32,32,32,32,32, + 32,32,32,33,33,33,33,33,33,33,33,34,34,34,34,34, + 34,34,34,35,35,35,35,35,35,35,35,36,36,36,36,36, + 36,36,36,37,37,37,37,37,37,37,38,38,38,38,38,38, + 38,39,39,39,39,39,39,39,40,40,40,40,40,40,40,41, + 41,41,41,41,41,41,42,42,42,42,42,42,43,43,43,43, + 43,43,43,44,44,44,44,44,44,45,45,45,45,45,45,46, + 46,46,46,46,46,47,47,47,47,47,47,48,48,48,48,48, + 48,49,49,49,49,49,50,50,50,50,50,50,51,51,51,51, + 51,52,52,52,52,52,53,53,53,53,53,54,54,54,54,54, + 55,55,55,55,55,56,56,56,56,56,57,57,57,57,57,58, + 58,58,58,58,59,59,59,59,59,60,60,60,60,61,61,61, + 61,61,62,62,62,62,63,63,63,63,64,64,64,64,64,65, + 65,65,65,66,66,66,66,67,67,67,67,68,68,68,68,69, + 69,69,69,70,70,70,70,71,71,71,71,72,72,72,72,73, + 73,73,73,74,74,74,75,75,75,75,76,76,76,76,77,77, + 77,78,78,78,78,79,79,79,80,80,80,80,81,81,81,82, + 82,82,83,83,83,83,84,84,84,85,85,85,86,86,86,86, + 87,87,87,88,88,88,89,89,89,90,90,90,91,91,91,92, + 92,92,93,93,93,94,94,94,95,95,95,96,96,96,97,97, + 97,98,98,99,99,99,100,100,100,101,101,101,102,102,103,103, + 103,104,104,104,105,105,106,106,106,107,107,108,108,108,109,109, + 110,110,110,111,111,112,112,112,113,113,114,114,114,115,115,116, + 116,116,117,117,118,118,119,119,119,120,120,121,121,122,122,123, + 123,123,124,124,125,125,126,126,127,127,128,128,128,129,129,130, + 130,131,131,132,132,133,133,134,134,135,135,136,136,137,137,138, + 138,139,139,140,140,141,141,142,142,143,143,144,144,145,145,146, + 146,147,147,148,148,149,150,150,151,151,152,152,153,153,154,155, + 155,156,156,157,157,158,158,159,160,160,161,161,162,163,163,164, + 164,165,166,166,167,167,168,169,169,170,170,171,172,172,173,173, + 174,175,175,176,177,177,178,179,179,180,181,181,182,183,183,184, + 184,185,186,187,187,188,189,189,190,191,191,192,193,193,194,195, + 195,196,197,198,198,199,200,201,201,202,203,203,204,205,206,206, + 207,208,209,209,210,211,212,212,213,214,215,216,216,217,218,219, + 220,220,221,222,223,224,224,225,226,227,228,228,229,230,231,232, + 233,233,234,235,236,237,238,239,239,240,241,242,243,244,245,246, + 246,247,248,249,250,251,252,253,254,255,256,256,257,258,259,260, + 261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276, + 277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292, + 293,294,295,296,297,299,300,301,302,303,304,305,306,307,308,310, + 311,312,313,314,315,316,317,319,320,321,322,323,324,326,327,328, + 329,330,332,333,334,335,336,338,339,340,341,342,344,345,346,347, + 349,350,351,353,354,355,356,358,359,360,362,363,364,366,367,368, + 369,371,372,374,375,376,378,379,380,382,383,384,386,387,389,390, + 391,393,394,396,397,399,400,402,403,404,406,407,409,410,412,413, + 415,416,418,419,421,422,424,425,427,429,430,432,433,435,436,438, + 440,441,443,444,446,448,449,451,452,454,456,457,459,461,462,464, + 466,467,469,471,472,474,476,478,479,481,483,485,486,488,490,492, + 493,495,497,499,501,502,504,506,508,510,512,513,515,517,519,521, + 523,525,527,528,530,532,534,536,538,540,542,544,546,548,550,552, + 554,556,558,560,562,564,566,568,570,572,574,576,578,581,583,585, + 587,589,591,593,595,598,600,602,604,606,608,611,613,615,617,620, + 622,624,626,629,631,633,635,638,640,642,645,647,649,652,654,656, + 659,661,664,666,668,671,673,676,678,681,683,685,688,690,693,695, + 698,700,703,706,708,711,713,716,718,721,724,726,729,732,734,737, + 739,742,745,748,750,753,756,758,761,764,767,769,772,775,778,781, + 783,786,789,792,795,798,801,804,806,809,812,815,818,821,824,827, + 830,833,836,839,842,845,848,851,854,858,861,864,867,870,873,876, + 880,883,886,889,892,896,899,902,905,909,912,915,918,922,925,928, + 932,935,939,942,945,949,952,956,959,963,966,970,973,977,980,984, + 987,991,994,998,1002,1005,1009,1013,1016,1020,1024,1027,1031,1035,1038,1042, + 1046,1050,1054,1057,1061,1065,1069,1073,1077,1081,1084,1088,1092,1096,1100,1104, + 1108,1112,1116,1120,1124,1128,1133,1137,1141,1145,1149,1153,1157,1162,1166,1170, + 1174,1178,1183,1187,1191,1196,1200,1204,1209,1213,1217,1222,1226,1231,1235,1240, + 1244,1249,1253,1258,1262,1267,1271,1276,1280,1285,1290,1294,1299,1304,1309,1313, + 1318,1323,1328,1332,1337,1342,1347,1352,1357,1362,1366,1371,1376,1381,1386,1391, + 1396,1401,1407,1412,1417,1422,1427,1432,1437,1443,1448,1453,1458,1464,1469,1474, + 1479,1485,1490,1496,1501,1506,1512,1517,1523,1528,1534,1539,1545,1551,1556,1562, + 1567,1573,1579,1585,1590,1596,1602,1608,1613,1619,1625,1631,1637,1643,1649,1655, + 1661,1667,1673,1679,1685,1691,1697,1703,1709,1716,1722,1728,1734,1741,1747,1753, + 1760,1766,1772,1779,1785,1792,1798,1805,1811,1818,1824,1831,1837,1844,1851,1857, + 1864,1871,1878,1884,1891,1898,1905,1912,1919,1926,1933,1940,1947,1954,1961,1968, + 1975,1982,1989,1997,2004,2011,2018,2026,2033,2040,2048,2055,2062,2070,2077,2085, + 2093,2100,2108,2115,2123,2131,2138,2146,2154,2162,2169,2177,2185,2193,2201,2209, + 2217,2225,2233,2241,2249,2257,2266,2274,2282,2290,2298,2307,2315,2324,2332,2340, + 2349,2357,2366,2374,2383,2392,2400,2409,2418,2426,2435,2444,2453,2462,2471,2480, + 2489,2498,2507,2516,2525,2534,2543,2552,2561,2571,2580,2589,2599,2608,2618,2627, + 2637,2646,2656,2665,2675,2685,2694,2704,2714,2724,2733,2743,2753,2763,2773,2783, + 2793,2803,2814,2824,2834,2844,2855,2865,2875,2886,2896,2907,2917,2928,2938,2949, + 2959,2970,2981,2992,3003,3013,3024,3035,3046,3057,3068,3079,3091,3102,3113,3124, + 3135,3147,3158,3170,3181,3193,3204,3216,3227,3239,3251,3263,3274,3286,3298,3310, + 3322,3334,3346,3358,3370,3382,3395,3407,3419,3432,3444,3457,3469,3482,3494,3507, + 3520,3532,3545,3558,3571,3584,3597,3610,3623,3636,3649,3662,3675,3689,3702,3715, + 3729,3742,3756,3769,3783,3797,3810,3824,3838,3852,3866,3880,3894,3908,3922,3936, + 3951,3965,3979,3994,4008,4023,4037,4052,4066,4081,4096,4111,4125,4140,4155,4170, + 4186,4201,4216,4231,4246,4262,4277,4293,4308,4324,4339,4355,4371,4387,4403,4418, + 4434,4450,4467,4483,4499,4515,4532,4548,4564,4581,4597,4614,4631,4648,4664,4681, + 4698,4715,4732,4749,4766,4784,4801,4818,4836,4853,4871,4888,4906,4924,4942,4960, + 4978,4996,5014,5032,5050,5068,5087,5105,5123,5142,5161,5179,5198,5217,5236,5255, + 5274,5293,5312,5331,5350,5370,5389,5409,5428,5448,5467,5487,5507,5527,5547,5567, + 5587,5607,5628,5648,5668,5689,5710,5730,5751,5772,5793,5814,5835,5856,5877,5898, + 5919,5941,5962,5984,6006,6027,6049,6071,6093,6115,6137,6159,6182,6204,6226,6249, + 6271,6294,6317,6340,6363,6386,6409,6432,6455,6479,6502,6526,6549,6573,6597,6620, + 6644,6668,6693,6717,6741,6765,6790,6814,6839,6864,6889,6914,6939,6964,6989,7014, + 7040,7065,7091,7116,7142,7168,7194,7220,7246,7272,7298,7325,7351,7378,7404,7431, + 7458,7485,7512,7539,7567,7594,7621,7649,7677,7704,7732,7760,7788,7817,7845,7873, + 7902,7930,7959,7988,8017,8046,8075,8104,8133,8163,8192,8222,8251,8281,8311,8341, + 8372,8402,8432,8463,8493,8524,8555,8586,8617,8648,8679,8711,8742,8774,8806,8837, + 8869,8901,8934,8966,8998,9031,9064,9096,9129,9162,9195,9229,9262,9296,9329,9363, + 9397,9431,9465,9499,9533,9568,9603,9637,9672,9707,9742,9777,9813,9848,9884,9920, + 9956,9992,10028,10064,10100,10137,10174,10210,10247,10284,10322,10359,10396,10434,10472,10510, + 10548,10586,10624,10662,10701,10740,10779,10818,10857,10896,10935,10975,11015,11054,11094,11135, + 11175,11215,11256,11296,11337,11378,11420,11461,11502,11544,11586,11628,11670,11712,11754,11797, + 11839,11882,11925,11968,12012,12055,12099,12142,12186,12230,12275,12319,12364,12408,12453,12498, + 12543,12589,12634,12680,12726,12772,12818,12864,12911,12958,13004,13052,13099,13146,13194,13241, + }; + + if ( note < 0 ) + note = 0; + if ( note > 127*16 ) + note = 127*16; + + return pFrequencies[note]; +} + +int Abs(int x) +{ + if ( x < 0 ) + return -x; + return x; +} + +int Sinus(int x) +{ + // translate x into [-pi, pi].f12 + while ( x > 12868 ) + x -= 25736; + while ( x < -12868 ) + x += 25736; + // compute sin(x) + // see http://www.devmaster.net/forums/showthread.php?t=5784 + const int B = 5215; + const int C = -1660; + return (B*x + ((C*x)>>12)*Abs(x))>>12; +} + +void MemCopy(void * dst, void * src, int len) +{ + unsigned char * d = (unsigned char *) dst; + unsigned char * s = (unsigned char *) src; + + while ( len-- ) + *d++ = *s++; +} + diff --git a/Player/arm7/source/Utils.h b/Player/arm7/source/Utils.h new file mode 100644 index 0000000..5f05020 --- /dev/null +++ b/Player/arm7/source/Utils.h @@ -0,0 +1,9 @@ +#ifndef _MIDI_H_ +#define _MIDI_H_ + +unsigned short MidiNoteToFrequency(int note); +int Abs(int x); +int Sinus(int x); // fixed point .12 +void MemCopy(void * pDestination, void * pSource, int iSize); + +#endif // _MIDI_H_ diff --git a/Player/arm7/source/bios.s b/Player/arm7/source/bios.s new file mode 100644 index 0000000..0e3acc0 --- /dev/null +++ b/Player/arm7/source/bios.s @@ -0,0 +1,8 @@ + .text + .align 4 + .thumb + .global swiWaitVBL + .thumb_func +swiWaitVBL: + swi 0x05 + bx lr diff --git a/Player/arm7/source/main.c b/Player/arm7/source/main.c new file mode 100644 index 0000000..fc69db8 --- /dev/null +++ b/Player/arm7/source/main.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include "Player.h" + +void VCountHandler() +{ + inputGetAndSend(); +} + +int main() +{ + irqInit(); + fifoInit(); + + readUserSettings(); + initClockIRQ(); + + SetYtrigger(80); + + installSystemFIFO(); + + irqSet(IRQ_VCOUNT, VCountHandler); + irqSet(IRQ_VBLANK, 0); + irqEnable(IRQ_VBLANK|IRQ_VCOUNT|IRQ_NETWORK); + + powerOn(POWER_SOUND); + REG_SOUNDCNT = SOUND_ENABLE | SOUND_VOL(127); + + static Player oPlayer; + Player_Init(& oPlayer); + + while ( 1 ) + { + swiWaitForVBlank(); + + Player_Execute(& oPlayer); + } + + return 0; +} diff --git a/Player/arm9/Makefile b/Player/arm9/Makefile new file mode 100644 index 0000000..bb98a9c --- /dev/null +++ b/Player/arm9/Makefile @@ -0,0 +1,132 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +# DATA is a list of directories containing binary files +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD := build +SOURCES := source +INCLUDES := include +DATA := + + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb -mthumb-interwork + +CFLAGS := -g -Wall -O2\ + -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\ + -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM9 +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +ASFLAGS := -g $(ARCH) -march=armv5te -mtune=arm946e-s -I$(CURDIR)/../source + +LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lnds9 + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBNDS) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export ARM9BIN := $(TOPDIR)/$(TARGET).arm9 +export ARM9ELF := $(CURDIR)/$(TARGET).arm9.elf +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) *.elf *.nds* *.bin + + +#--------------------------------------------------------------------------------- +else + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(ARM9BIN) : $(ARM9ELF) + @$(OBJCOPY) -O binary $< $@ + @echo built ... $(notdir $@) + +$(ARM9ELF) : $(OFILES) + @echo linking $(notdir $@) + @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPSDIR)/*.d + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/Player/arm9/source/basicloop.ns b/Player/arm9/source/basicloop.ns new file mode 100644 index 0000000..9d972d8 Binary files /dev/null and b/Player/arm9/source/basicloop.ns differ diff --git a/Player/arm9/source/main.cpp b/Player/arm9/source/main.cpp new file mode 100644 index 0000000..6e88042 --- /dev/null +++ b/Player/arm9/source/main.cpp @@ -0,0 +1,34 @@ +#include +#include + +extern unsigned char basicloop[]; + +int main() +{ + powerOn(POWER_ALL); + + videoSetMode(MODE_0_2D); + + consoleDemoInit(); + + BG_PALETTE[0] = RGB15(0, 0, 0); + + while ( 1 ) + { + swiWaitForVBlank(); + scanKeys(); + + if ( keysDown() & KEY_X ) + { + fifoSendValue32(FIFO_USER_01, (u32)(& basicloop[0])); + BG_PALETTE[0] = RGB15(15, 0, 0); + } + else if ( keysHeld() & KEY_B ) + { + fifoSendValue32(FIFO_USER_01, 0); + BG_PALETTE[0] = RGB15(0, 0, 0); + } + } + + return 0; +} diff --git a/Player/arm9/source/tunes.s b/Player/arm9/source/tunes.s new file mode 100644 index 0000000..603cec8 --- /dev/null +++ b/Player/arm9/source/tunes.s @@ -0,0 +1,5 @@ + .section .rodata + .balign 4 + .global basicloop +basicloop: + .incbin "test"