Permalink
Browse files

Implement portamento

  • Loading branch information...
1 parent 926215d commit 3e80b79d1b4c9fd93542d2486597c1e03f8aa4df @fincs committed Mar 24, 2012
Showing with 101 additions and 18 deletions.
  1. +3 −1 README.md
  2. +28 −6 arm7/source/sndbase.arm.c
  3. +66 −11 arm7/source/sseq.arm.c
  4. +4 −0 common/sndcommon.h
View
@@ -17,12 +17,14 @@ Running the SSEQ player
-----------------------
If the .nds file is run with no parameters, it attempts to play the hardcoded files inside its NitroFS. Alternatively you can pass the paths to the sseq file, the sbnk file and the swar files (in that order) through the ARGV mechanism. In order to do so, use HomebrewMenu .argv files.
+Note: recent devkitARM versions produce code that is choked on by no$gba. Use a different DS emulator or *cough* real hardware.
To do
-----
- Note dropping bug appears to be gone... I hope.
-- Some SSEQ commands are not implemented yet (portamento, pitch sweep): please help!
+- All commands referring to variables/math are still completely unsupported, because I still need to do the research.
+- The code really needs a rewrite.
Thanks to:
----------
View
@@ -121,15 +121,29 @@ static void ADSR_tickchn(int ch)
}
// Update the modulation params
- int modParam = 0, modType = chstat->modType;
+ int modParam = 0, modType = chstat->modType, addPitch = 0;
do
{
+ if (!chstat->modDepth)
+ break;
+
if (chstat->modDelayCnt < chstat->modDelay)
{
chstat->modDelayCnt ++;
break;
}
+ // Get the current modulation parameter
+ modParam = GetSoundSine(chstat->modCounter >> 8) * chstat->modRange * chstat->modDepth;
+
+ if (modType == 0)
+ addPitch = (s64)(modParam * 60) >> 14;
+ else
+ // This ugly formula whose exact meaning and workings I cannot figure out are used for pitch/volume modulation.
+ modParam = ((modParam &~ 0xFC000000) >> 8) | ((((modParam < 0 ? -1 : 0) << 6) | ((u32)modParam >> 26)) << 18);
+
+ // Update the modulation variables
+
u16 speed = (u16)chstat->modSpeed << 6;
u16 counter = (chstat->modCounter + speed) >> 8;
@@ -139,11 +153,19 @@ static void ADSR_tickchn(int ch)
chstat->modCounter += speed;
chstat->modCounter &= 0xFF;
chstat->modCounter |= counter << 8;
-
- modParam = GetSoundSine(chstat->modCounter >> 8) * chstat->modRange * chstat->modDepth;
}while(0);
- modParam >>= 8;
+ if (chstat->sweepPitch != 0)
+ {
+ u32 cnt = chstat->sweepCnt;
+ u32 len = chstat->sweepLen;
+ if (cnt <= len)
+ {
+ addPitch += (s32)(((s64)chstat->sweepPitch*(len-cnt))/len);
+ if (cnt < len)
+ chstat->sweepCnt ++;
+ }
+ }
#ifdef LOG_SEQ
char buf[30];
@@ -174,7 +196,7 @@ static void ADSR_tickchn(int ch)
if (pan < 0) pan = 0;
if (pan > 127) pan = 127;
- u32 cr = SCHANNEL_CR(ch) &~ (SOUND_VOL(0x7F) | SOUND_VOLDIV(3) | SOUND_PAN(0x7F));
+ u32 cr = REG.CR &~ (SOUND_VOL(0x7F) | SOUND_VOLDIV(3) | SOUND_PAN(0x7F));
cr |= SOUND_VOL(res) | SOUND_PAN(pan);
if (totalvol < (-240 + 723)) cr |= SOUND_VOLDIV(3);
else if (totalvol < (-120 + 723)) cr |= SOUND_VOLDIV(2);
@@ -183,7 +205,7 @@ static void ADSR_tickchn(int ch)
ADSR_vol[ch] = ((cr & SOUND_VOL(0x7F)) << 4) >> ((cr & SOUND_VOLDIV(3)) >> 8);
SCHANNEL_CR(ch) = cr;
u16 timer = REG.TIMER;
- if (modType == 0) timer = AdjustFreq(timer, modParam);
+ if (addPitch) timer = AdjustFreq(timer, addPitch);
SCHANNEL_TIMER(ch) = -timer;
#undef AMPL
View
@@ -97,13 +97,16 @@ typedef struct
{
int count;
int pos;
- int ret;
int prio;
u16 patch;
u16 waitmode;
playinfo_t playinfo;
int a,d,s,r;
int loopcount,looppos;
+ int ret[3];
+ int retpos;
+ u8 portakey, portatime;
+ s16 sweepPitch;
} trackstat_t;
int ntracks = 0;
@@ -112,6 +115,8 @@ void* seqBnk = NULL;
void* seqWar[4] = {NULL, NULL, NULL, NULL};
trackstat_t tracks[16];
+void seq_updatechnporta(ADSR_stat_t* chstat, trackstat_t* pTrack);
+
int _Note(void* bnk, void** war, int instr, int note, int prio, playinfo_t* playinfo, int duration, int track)
{
int isPsg = 0;
@@ -211,6 +216,10 @@ int _Note(void* bnk, void** war, int instr, int note, int prio, playinfo_t* play
chstat->prio = prio;
chstat->count = duration;
chstat->track = track;
+ chstat->note = note;
+ chstat->patch = instr;
+ seq_updatechnporta(chstat, pTrack);
+ pTrack->portakey = note | (pTrack->portakey & 0x80);
chstat->state = ADSR_START;
return ch;
@@ -242,6 +251,9 @@ static inline void PrepareTrack(int i, int pos)
tracks[i].playinfo.modSpeed = 16;
tracks[i].playinfo.modDelay = 10;
tracks[i].prio = 64;
+ tracks[i].retpos = 0;
+ tracks[i].portakey = 0x80;
+ tracks[i].portatime = 0;
tracks[i].a = -1; tracks[i].d = -1; tracks[i].s = -1; tracks[i].r = -1;
}
@@ -354,6 +366,30 @@ void seq_updatemodulation(int track, playinfo_t* info, int what)
}
}
+void seq_updatechnporta(ADSR_stat_t* chstat, trackstat_t* pTrack)
+{
+ chstat->sweepPitch = pTrack->sweepPitch;
+ if (pTrack->portakey & 0x80)
+ {
+ chstat->sweepLen = 0;
+ chstat->sweepCnt = 0;
+ return;
+ }
+
+ int diff = ((int)pTrack->portakey - (int)chstat->note) << 22;
+ chstat->sweepPitch += diff >> 16;
+
+ if (pTrack->portatime == 0)
+ chstat->sweepLen = (chstat->count * 240 + seq_bpm - 1) / seq_bpm;
+ else
+ {
+ u32 sq_time = pTrack->portatime * pTrack->portatime;
+ int abs_sp = chstat->sweepPitch;
+ abs_sp = abs_sp < 0 ? -abs_sp : abs_sp;
+ chstat->sweepLen = (abs_sp*sq_time) >> 11;
+ }
+}
+
void track_tick(int n)
{
trackstat_t* track = tracks + n;
@@ -420,7 +456,7 @@ void track_tick(int n)
nocashMessage("CALL");
#endif
int dest = SEQ_READ24(track->pos);
- track->ret = track->pos + 3;
+ track->ret[track->retpos++] = track->pos + 3;
track->pos = dest;
break;
}
@@ -468,7 +504,7 @@ void track_tick(int n)
#ifdef LOG_SEQ
nocashMessage("RET");
#endif
- track->pos = track->ret;
+ track->pos = track->ret[--track->retpos];
break;
}
case 0xC0: // PAN
@@ -499,9 +535,6 @@ void track_tick(int n)
}
case 0xC3: // TRANSPOSE
case 0xC8: // TIE
- case 0xC9: // PORTAMENTO
- case 0xCE: // PORTAMENTO ON/OFF
- case 0xCF: // PORTAMENTO TIME
case 0xD6: // PRINT VAR
{
// TODO
@@ -511,12 +544,36 @@ void track_tick(int n)
track->pos ++;
break;
}
+ case 0xC9: // PORTAMENTO
+ {
+#ifdef LOG_SEQ
+ nocashMessage("PORTAMENTO");
+#endif
+ track->portakey = SEQ_READ8(track->pos); track->pos ++;
+ break;
+ }
+ case 0xCE: // PORTAMENTO ON/OFF
+ {
+#ifdef LOG_SEQ
+ nocashMessage("PORTAMENTO ON/OFF");
+#endif
+ track->portakey &= ~0x80;
+ track->portakey |= (!SEQ_READ8(track->pos)) << 7; track->pos ++;
+ break;
+ }
+ case 0xCF: // PORTAMENTO TIME
+ {
+#ifdef LOG_SEQ
+ nocashMessage("PORTAMENTO TIME");
+#endif
+ track->portatime = SEQ_READ8(track->pos); track->pos ++;
+ break;
+ }
case 0xC4: // PITCH BEND
{
#ifdef LOG_SEQ
nocashMessage("PITCH BEND");
#endif
-
track->playinfo.pitchb = (s8)SEQ_READ8(track->pos); track->pos ++;
seq_updatepitchbend(n, &track->playinfo);
break;
@@ -526,7 +583,6 @@ void track_tick(int n)
#ifdef LOG_SEQ
nocashMessage("PITCH BEND RANGE");
#endif
-
track->playinfo.pitchr = SEQ_READ8(track->pos); track->pos ++;
seq_updatepitchbend(n, &track->playinfo);
break;
@@ -658,11 +714,10 @@ void track_tick(int n)
}
case 0xE3: // SWEEP PITCH
{
- // TODO
#ifdef LOG_SEQ
- nocashMessage("DUMMY2");
+ nocashMessage("SWEEP PITCH");
#endif
- track->pos += 2;
+ track->sweepPitch = SEQ_READ16(track->pos); track->pos += 2;
break;
}
case 0xE1: // TEMPO
View
@@ -77,6 +77,10 @@ typedef struct
u8 modType, modSpeed, modDepth, modRange;
u16 modDelay, modDelayCnt, modCounter;
+ u8 note, patch;
+
+ u32 sweepLen, sweepCnt;
+ s16 sweepPitch;
sndreg_t reg;
} ADSR_stat_t;

0 comments on commit 3e80b79

Please sign in to comment.