/*typedef enum { final int RYMOD_CONSTANT=0; final int RYMOD_BETWEEN=1; final int RYMOD_PERIODIC=2; final int RYMOD_NONE=3; final int NUM_RYMODS=4; } RthmModify; // Hungarian: rymod */ //---------------------------------------------------------- import java.applet.Applet; class CRhythm extends Applet // Hungarian: rthm { int bogus_piDuration; // Bogus return value. // constants final int RYMOD_CONSTANT=0; final int RYMOD_BETWEEN=1; final int RYMOD_PERIODIC=2; final int RYMOD_NONE=3; final int NUM_RYMODS=4; final int BST_NO_NOTE=0; // No note on this beat final int BST_WHISPER=1; final int BST_LIGHT=2; // Faint note final int BST_MEDIUM=3; // Normal note final int BST_HEAVY=4; // Emphsized note final int BST_BANG=5; final double fMIN_NUMSLOTS =6.0; final double fMAX_NUMSLOTS =64.0; final double fMIN_DENSITY =.1; final double fMAX_DENSITY =1.0; final double fMIN_CONSISTENCY =.3; final double fMAX_CONSISTENCY =1.0; // Members int m_iLastBeatNum; // When was the last time we were played? int m_iLastIndexPlayed; // What was the last note we returned? char m_pvTempo; // This name is actually misleading. // The larger this value is, the slower we go. See // GetBeatNum and bstGetConstantStatus. int m_iNumNotes; // Number of notes actually played in an // occurence of this rhythm/meter. int m_iNumAllocated; int m_arriNoteBeatNums[]; // Array of beat numbers: one for // each of the notes to play, where m_arriNoteBeatNums[2]=3 // means the 2nd note will be played on the (3*m_pvTempo)'th // beat of the rhythm. // Assume beat numbers are in ascending order. int m_arrbstNoteStresses[]; // Array of stresses: one // for each of the notes to play. // The following chars are used for generating new rhythms. // None of these values specifically refer to a number, instead // they map into the range of allowable values. 0 for minimum, // 127 for max, 63-64 in the middle. char m_pvDensity; // What ratio of slots to fill. char m_pvLength; // How many slots to have available. // Directly proportional to periodicity. char m_pvConsistency; // Amount of repetition. //=============================================== public int mod (int x, int y) { if (x>=0) return x%y; int iNegMod = (-x) % y; return (0==iNegMod) ? y-iNegMod : 0; } public void assert(boolean b) { if (!b) m_arrbstNoteStresses[-1] = 0; } public int rand() { return (int)(Math.random() * 9747); } // Operations // Set when this occurence of the rhythm/meter will begin. void SetFirstBeat (int iBeatNum) { m_iLastBeatNum = iBeatNum; m_iLastIndexPlayed = -1; } int bstGetBeatStatus (int iCurrBeatNum, CRthmMods prmods, int piDuration) { bogus_piDuration = 0; assert(m_iNumNotes > 0); // For cadence, slowly become twice as slow by end of rhythm // (for now). char pvOldTempo = 0; boolean bCadence = /*prmods &&*/ prmods.bCadence; if (bCadence) { pvOldTempo = m_pvTempo; double fRatio = ((double)m_iLastIndexPlayed+1)/ ((double)m_iNumNotes); if (fRatio < .0) fRatio = .0; else if (fRatio > 1.0) fRatio = 1.0; m_pvTempo = (char)(((double)m_pvTempo) * (fRatio+1.0) + .5); //tw.spr("GBS: %d(%d) -> %d\r", iBeatNum, pvOldTempo, m_pvTempo); } int iBeatNum = iCurrBeatNum - m_iLastBeatNum; if (m_iLastIndexPlayed != -1) iBeatNum += GetBeatNum(m_iLastIndexPlayed); assert(iBeatNum >= 0); int ret = -1; switch (prmods.rymod) { case RYMOD_CONSTANT: ret = bstGetConstantStatus(iBeatNum, prmods.fRMArg, piDuration); break; case RYMOD_BETWEEN: ret = bstGetBetweenStatus(iBeatNum, prmods.fRMArg, piDuration); break; case RYMOD_PERIODIC: ret = bstGetPeriodicStatus(iBeatNum, prmods.fRMArg, piDuration); break; case RYMOD_NONE: ret = bstGetNormalStatus(iBeatNum, piDuration); // How about a volume change for normal cadence? if (bCadence) { if (ret != BST_NO_NOTE && ret != BST_BANG) ret = (ret+1); } break; default: assert(false); break; } // Should we randomly insert a note where one shouldn't be? if (ret == BST_NO_NOTE && iBeatNum%m_pvTempo == 0) { double fRand = ((double)m_arriNoteBeatNums[m_iNumNotes-1] - m_arriNoteBeatNums[0]); fRand *= (double)(prmods.pvCoherence)*6.0/127.0; if (fRand < 1.0) fRand = 1.0; if (mod(rand(), (int)(fRand + .5)) == 0) { ret = BST_LIGHT; } } // Return speed to normal. if (bCadence) { m_pvTempo = pvOldTempo; } if (ret != BST_NO_NOTE && bogus_piDuration == 0) bogus_piDuration = m_pvTempo; return ret; } // Return -1 if the given beat number is before the current // iteration of the rhythm; 0 if it fits within; 1 if it // occurs after. public int iOutside (int iBeatNum, CRthmMods prmods/*=0*/) { double fPos = fPosition(iBeatNum, prmods); if (fPos <= 0) return -1; if (fPos <= 1) return 0; return 1; } public double fPosition (int iCurrBeatNum, CRthmMods prmods/*=0*/) { // For cadence, slowly become twice as slow by end of rhythm // (for now). assert(m_iNumNotes > 0); char pvOldTempo = 0; boolean bCadence = /*prmods && */prmods.bCadence; if (bCadence) { pvOldTempo = m_pvTempo; double fRatio = (double)(m_iLastIndexPlayed+1)/ (double)(m_iNumNotes); if (fRatio < .0) fRatio = .0; else if (fRatio > 1.0) fRatio = 1.0; m_pvTempo = (char)((double)(m_pvTempo) * (fRatio+1.0) +.5); //tw.spr("GBS: %d(%d) -> %d\r", iBeatNum, pvOldTempo, m_pvTempo); } int iBeatNum = iCurrBeatNum - m_iLastBeatNum; if (m_iLastIndexPlayed != -1) iBeatNum += GetBeatNum(m_iLastIndexPlayed); assert(iBeatNum >= 0); double ret = (double)(iBeatNum) / (double)(GetBeatNum(m_iNumNotes-1)); // Return speed to normal. if (bCadence) m_pvTempo = pvOldTempo; return ret; } public boolean bLastNote (int iCurrBeatNum, CRthmMods prmods/*=0*/) { return m_iLastIndexPlayed >= m_iNumNotes-2; } public void SetTempo (char pvTempo) { m_pvTempo = pvTempo; } public char pvGetTempo () { return m_pvTempo; } public char pvDensity () { return m_pvDensity; } public char pvLength () { return m_pvLength; } public int iNormalLength() { return GetBeatNum(m_iNumNotes-1); } public char pvConsistency () { return m_pvConsistency; } public int iAveDuration () { int ret = GetBeatNum(m_iNumNotes-1)/m_iNumNotes; if (ret!=0) return ret; else return 1; } // Construction/Destruction public CRhythm () { m_iLastBeatNum=-1; m_iLastIndexPlayed=-1; m_pvTempo=1; m_iNumNotes=0; m_iNumAllocated=0; m_arriNoteBeatNums=null; m_arrbstNoteStresses=null; m_pvDensity=64; m_pvLength=64; m_pvConsistency=64; } //CRhythm (char* szInput); //int bSet (const char* szInput); public boolean bSet (CRhythm rthm) { m_iLastBeatNum = rthm.m_iLastBeatNum; m_iLastIndexPlayed = rthm.m_iLastIndexPlayed; m_pvTempo = rthm.m_pvTempo; m_iNumNotes = rthm.m_iNumNotes; m_iNumAllocated = m_iNumNotes; m_arriNoteBeatNums = new int[m_iNumAllocated]; m_arrbstNoteStresses = new int[m_iNumAllocated]; for (int C=0; C fConsistency) { // Change interval... double fRand = (double)(rand()%200)/100.0 - 1.0; fCurrInterval = fRand*fAveInterval + fAveInterval; if (fCurrInterval < 1.0) fCurrInterval = 1.0; } iLastSlot += (int)(.5 + fCurrInterval); int iLowBound = (int)(.5 + (1.0-fConsistency)*33.0); int iHighBound = (int)(fConsistency*33.0 + 67.5); int iRand = rand()%100; // 0 to 99 int bst; if (iRand < iLowBound) bst = BST_LIGHT; else { if (iRand < iHighBound) bst = BST_MEDIUM; else bst = BST_HEAVY; } AppendNote(iLastSlot, bst); } // Mark end with whisper (altho the BST doesn't matter); AppendNote((int)(m_arriNoteBeatNums[iMaxNum-1] + fCurrInterval -1.0+.5), BST_WHISPER); return true; } // Implementation int bstGetNormalStatus (int iBeatNum, int piDuration) { if (m_iLastIndexPlayed >= m_iNumNotes - 1) return BST_NO_NOTE; if (GetBeatNum(m_iLastIndexPlayed+1) <= iBeatNum) { m_iLastBeatNum = m_iLastBeatNum + iBeatNum; if (m_iLastIndexPlayed != -1) m_iLastBeatNum -= GetBeatNum(m_iLastIndexPlayed); m_iLastIndexPlayed++; if (m_iLastIndexPlayed >= m_iNumNotes - 1) return BST_NO_NOTE; // Last note is just a marker. // Calculate duration: bogus_piDuration = (int)((GetBeatNum(m_iLastIndexPlayed+1) - GetBeatNum(m_iLastIndexPlayed))*1.1); // Last note plays until next line's 1st note: if (m_iLastIndexPlayed == m_iNumNotes - 2) bogus_piDuration += GetBeatNum(0) * 1.1; return m_arrbstNoteStresses[m_iLastIndexPlayed]; } return BST_NO_NOTE; } int bstGetConstantStatus (int iBeatNum, double fArg, int piDuration) { if (m_iLastIndexPlayed >= m_iNumNotes - 1) return BST_NO_NOTE; if (GetBeatNum(m_iLastIndexPlayed+1) <= iBeatNum) { // Update this as if we were normally playing. m_iLastBeatNum = m_iLastBeatNum + iBeatNum; if (m_iLastIndexPlayed != -1) m_iLastBeatNum -= GetBeatNum(m_iLastIndexPlayed); m_iLastIndexPlayed++; if (m_iLastIndexPlayed >= m_iNumNotes - 1) return BST_NO_NOTE; // End: Last note is just a marker. } int iIntervalLen = (int)(.5 + fArg*m_pvTempo); if (iBeatNum % iIntervalLen == 0) { bogus_piDuration = (int)(1.1 * iIntervalLen); if (iBeatNum % (iIntervalLen*2) == 0 && mod(rand(),4) == 0) return BST_MEDIUM; return BST_LIGHT; } return BST_NO_NOTE; } int bstGetBetweenStatus (int iBeatNum, double fArg, int piDuration) { if (m_iLastIndexPlayed >= m_iNumNotes - 1) return BST_NO_NOTE; if (GetBeatNum(m_iLastIndexPlayed+1) <= iBeatNum) { // Update this as if we were normally playing. m_iLastBeatNum = m_iLastBeatNum + iBeatNum; if (m_iLastIndexPlayed != -1) m_iLastBeatNum -= GetBeatNum(m_iLastIndexPlayed); m_iLastIndexPlayed++; if (m_iLastIndexPlayed >= m_iNumNotes - 1) return BST_NO_NOTE; // End: Last note is just a marker. } int iPrevBeat; if (m_iLastIndexPlayed == -1) iPrevBeat = 0; else iPrevBeat = GetBeatNum(m_iLastIndexPlayed); int iNextBeat = GetBeatNum(m_iLastIndexPlayed+1); int iDist = (int)(.5 + (double)(iNextBeat - iPrevBeat) * fArg); if (iPrevBeat + iDist == iBeatNum) { bogus_piDuration = (int)(1.1 * (iNextBeat - iPrevBeat)); return BST_LIGHT; } return BST_NO_NOTE; } int bstGetPeriodicStatus (int iBeatNum, double fArg, int piDuration) { if (m_iLastIndexPlayed >= m_iNumNotes - 1) return BST_NO_NOTE; if (GetBeatNum(m_iLastIndexPlayed+1) <= iBeatNum) { // Update this as if we were normally playing. m_iLastBeatNum = m_iLastBeatNum + iBeatNum; if (m_iLastIndexPlayed != -1) m_iLastBeatNum -= GetBeatNum(m_iLastIndexPlayed); m_iLastIndexPlayed++; if (m_iLastIndexPlayed >= m_iNumNotes - 1) return BST_NO_NOTE; // End: Last note is just a marker. } if (iBeatNum % m_pvTempo != 0) return BST_NO_NOTE; iBeatNum /= m_pvTempo; // Switch to internal time units. int iPeriodLen = (int)(.5 + fArg); // Which beat in the subrhythm am I on? iBeatNum = mod(iBeatNum, iPeriodLen); // Depends on m_pvDensity, m_pvConsistency, iBeatNum. double fDensity = (double)((int)m_pvDensity); fDensity = /*fMIN_DENSITY +*/ (fMAX_DENSITY-fMIN_DENSITY) * fDensity / 127.0; double fConsistency = (double)((int)m_pvConsistency); fConsistency = fConsistency/127.0; int iAveInterval = (int)(.5 + 1.0/fDensity); // Hash depends fully on iBeatNum/iAveInterval double fHash = (iBeatNum/iAveInterval) ^ (m_pvConsistency<<2) ^ m_pvLength ^ (m_pvDensity<<3); // A number between 0 and iPeriodLen - 1... fHash = (1.0 - fConsistency) * (double)((int)(fHash) % iAveInterval); if (iBeatNum % iAveInterval == (int)(fHash+.5)) { bogus_piDuration = (int)((double)(iAveInterval) * 1.1) * m_pvTempo; //TRACE("(+%d,%d,%.1f)", iBeatNum, iAveInterval, fHash); return BST_LIGHT; } //TRACE("_-%d,%d,%.1f_", iBeatNum, iAveInterval, fHash); return BST_NO_NOTE; } void AppendNote (int iBeatNum, int bst) { assert(bst != BST_NO_NOTE); assert(iBeatNum > 0); assert (0 <= m_iNumNotes || m_arriNoteBeatNums[m_iNumNotes-1] <= iBeatNum); if (m_iNumNotes == m_iNumAllocated) { m_iNumAllocated += 10; int[] temp = m_arriNoteBeatNums; m_arriNoteBeatNums = new int[m_iNumAllocated]; for (int C=0; C