ref: e8304ea686d212ad118f760dc9e6df5167027272
dir: /it_music.c/
/* Copyright (C) 2014, Jeffrey Lim. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "it_struc.h" // temporary shit to make this work --GM it_engine *ITEngineNew(void) { int i; it_engine *ite = calloc(sizeof(it_engine), 1); // TODO: initialise properly for(i = 0; i < 200; i++) ite->pat[i] = NULL; for(i = 0; i < 100; i++) ite->SamplePointer[i] = NULL; ite->NumChannels = 256; // shifting this here so it doesn't try to use a real unpacked pattern --GM ite->CurrentEditPattern = 199; ite->PatternNumber = 199; ite->CmdLineNumChannels = -1; return ite; } it_drvdata *drv_oss_init(it_engine *ite); it_drvdata *drv_sdl_init(it_engine *ite); it_drvdata *DriverSys_GetByName(it_engine *ite, const char *fname) { printf("drv: %s\n", fname); if(!strcmp(fname, "oss")) return drv_oss_init(ite); if(!strcmp(fname, "sdl")) return drv_sdl_init(ite); return NULL; } it_pattern *PE_GetCurrentPattern(it_engine *ite, uint16_t *patnum, uint16_t *patrow) { *patnum = ite->PatternNumber; *patrow = ite->MaxRow + 1; return NULL; // actually meant to return UNPACKED pattern data area --GM } uint16_t PE_GetLastInstrument(it_engine *ite) { return ite->LastInstrument-1; } // // Functions for playing control // Music_PlaySong........ parameters, AX = order // Music_Stop............ parameters, None // Music_PlayPattern..... parameters, AX = pattern, BX = number of rows, CX = row // Music_ToggleChannel... parameters, AX = channel // Music_SoloChannel..... parameters, AX = channel // const int8_t FineSineData[] = { 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, 0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23, -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2, }; const int8_t FineRampDownData[] = { 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8, -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16, -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24, -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32, -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40, -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48, -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56, -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64, }; const int8_t FineSquareWave[] = { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; const uint8_t EmptyPattern[] = { 64, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // Zero globals. const float PitchDepthConstant = 98304.0f; // TODO: Turn these two into proper structs uint8_t InstrumentHeader[] = { 'I', 'M', 'P', 'I', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 128, 32+128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32, 0, 33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40, 0, 41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48, 0, 49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0, 57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64, 0, 65, 0, 66, 0, 67, 0, 68, 0, 69, 0, 70, 0, 71, 0, 72, 0, 73, 0, 74, 0, 75, 0, 76, 0, 77, 0, 78, 0, 79, 0, 80, 0, 81, 0, 82, 0, 83, 0, 84, 0, 85, 0, 86, 0, 87, 0, 88, 0, 89, 0, 90, 0, 91, 0, 92, 0, 93, 0, 94, 0, 95, 0, 96, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 105, 0, 106, 0, 107, 0, 108, 0, 109, 0, 110, 0, 111, 0, 112, 0, 113, 0, 114, 0, 115, 0, 116, 0, 117, 0, 118, 0, 119, 0, 0, 2, 0, 0, 0, 0, 64, 0, 0, 64, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; uint8_t SampleHeader[80] = { 'I', 'M', 'P', 'S', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (8363&0xFF), (8363>>8), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; uint8_t MIDIPitchSendString[] = {0x65, 0x00, 0x64, 0x00, 0x06}; const char *PrepareSamplesMsg = "Preparing Samples"; const char *ReverseMsg = "Left/right outputs reversed"; const char *NoSoundCardMsg = " No sound card detected"; const char *MIDIConfigFileName = "ITMIDI.CFG"; const char *OrderUpdateEnabledMsg = "Order list unlocked"; const char *OrderUpdateDisabledMsg = "Order list locked"; const char *UnsoloMsg = "Solo disabled"; const char *SoloSampleMsg = "Solo sample \375D"; const char *SoloInstrumentMsg = "Solo instrument \375D"; #ifdef DEBUG const char *LoadDriverMessage = "Loading driver:"; const char *UnableToReadFileMessage = "Unable to read file"; const char *DetectingMessage = "Testing driver"; //uint16_t ScreenOffset = 0; #endif const char *PleaseWaitMsg = "Please Wait..."; const char *PCSpeakerDriver = "ITPCSPKR.DRV"; const char *SBDriver = "ITSB.DRV"; const char *SB2Driver = "ITSB2.DRV"; const char *SBProDriver = "ITSBPRO.DRV"; const char *SB16Driver = "ITSB16.DRV"; const char *AWE32Driver = "ITAWE32.DRV"; const char *GUSDriver = "ITGUS.DRV"; const char *InterwaveDriver = "ITIW.DRV"; const char *PASDriver = "ITPAS.DRV"; const char *PAS16Driver = "ITPAS16.DRV"; const char *WSSDriver = "ITWSS.DRV"; const char *ESSDriver = "ITES1868.DRV"; const char *MIDIDriver = "ITMPU401.DRV"; const char *EWSCodecDriver = "ITEWSCOD.DRV"; const char *VIVOCodecDriver = "ITVIVO.DRV"; const char *ST97PCICodecDriver = "ITSTCODE.DRV"; const char *WAVDriver = "ITWAV.DRV"; const char *MIDDriver = "ITMID.DRV"; const char *VSoundMMXDriver = "ITVSOUND.MMX"; const char *VSoundDriver = "ITVSOUND.DRV"; const char *DefaultDriver = "ITSOUND.DRV"; #if 0 const char *DriverNameTable[] = { NULL, "ITPAS16.DRV", "ITSB16.DRV", "ITIW.DRV", "ITGUS.DRV", "ITAWE32.DRV", "ITSBPRO.DRV", "ITSB.DRV", "ITPCSPKR.DRV", "ITSB2.DRV", "ITPAS.DRV", "ITWAV.DRV", "ITWSS.DRV", "ITES1868.DRV", "ITMPU401.DRV", "ITEWSCOD.DRV", "ITVIVO.DRV", "ITSTCODE.DRV", "ITMID.DRV", "ITSOUND.DRV", "ITVSOUND.MMX", "ITVSOUND.DRV", /* PAS16Driver, SB16Driver, InterwaveDriver, GUSDriver, AWE32Driver, SBProDriver, SBDriver, PCSpeakerDriver, SB2Driver, PASDriver, WAVDriver, WSSDriver, ESSDriver, MIDIDriver, EWSCodecDriver, VIVOCodecDriver, ST97PCICodecDriver, MIDDriver, DefaultDriver, VSoundMMXDriver, VSoundDriver, */ }; const uint16_t DriverDetectionOrder[] = { 19, 20, 21, 17, 16, 15, 13, 1, 10, 2, 12, 3, 4, 5, 6, 9, 7, 8, 0xFFFF, }; const uint16_t DriverSoundCard[] = { 0, 8, 7, 9, 6, 2, 5, 4, 3, 10, 1, 12, 13, 15, 16, 0, 0, 0, 0, 14, 11, 18, 0xFFFF, }; #else const char *DriverNameTable[] = { NULL, "oss", "sdl", // (DONE) I hope to get SDL audio working at some point. // Possibly also a winmm driver for Windows. // JACK will definitely be there, too! Will use ezjack for that. }; const uint16_t DriverDetectionOrder[] = { 2, 1, 0, 0xFFFF, }; const uint16_t DriverSoundCard[] = { 0, 0, 1, 0xFFFF, }; #endif // TODO: translate this properly! const uint32_t PitchTable[] = { 2048+((0)<<16), 2170+((0)<<16), 2299+((0)<<16), 2435+((0)<<16), 2580+((0)<<16), 2734+((0)<<16), 2896+((0)<<16), 3069+((0)<<16), 3251+((0)<<16), 3444+((0)<<16), 3649+((0)<<16), 3866+((0)<<16), 4096+((0)<<16), 4340+((0)<<16), 4598+((0)<<16), 4871+((0)<<16), 5161+((0)<<16), 5468+((0)<<16), 5793+((0)<<16), 6137+((0)<<16), 6502+((0)<<16), 6889+((0)<<16), 7298+((0)<<16), 7732+((0)<<16), 8192+((0)<<16), 8679+((0)<<16), 9195+((0)<<16), 9742+((0)<<16), 10321+((0)<<16), 10935+((0)<<16), 11585+((0)<<16), 12274+((0)<<16), 13004+((0)<<16), 13777+((0)<<16), 14596+((0)<<16), 15464+((0)<<16), 16384+((0)<<16), 17358+((0)<<16), 18390+((0)<<16), 19484+((0)<<16), 20643+((0)<<16), 21870+((0)<<16), 23170+((0)<<16), 24548+((0)<<16), 26008+((0)<<16), 27554+((0)<<16), 29193+((0)<<16), 30929+((0)<<16), 32768+((0)<<16), 34716+((0)<<16), 36781+((0)<<16), 38968+((0)<<16), 41285+((0)<<16), 43740+((0)<<16), 46341+((0)<<16), 49097+((0)<<16), 52016+((0)<<16), 55109+((0)<<16), 58386+((0)<<16), 61858+((0)<<16), 0+((1)<<16), 3897+((1)<<16), 8026+((1)<<16), 12400+((1)<<16), 17034+((1)<<16), 21944+((1)<<16), 27146+((1)<<16), 32657+((1)<<16), 38496+((1)<<16), 44682+((1)<<16), 51236+((1)<<16), 58179+((1)<<16), 0+((2)<<16), 7794+((2)<<16), 16051+((2)<<16), 24800+((2)<<16), 34068+((2)<<16), 43888+((2)<<16), 54292+((2)<<16), 65314+((2)<<16), 11456+((3)<<16), 23828+((3)<<16), 36936+((3)<<16), 50823+((3)<<16), 0+((4)<<16), 15588+((4)<<16), 32103+((4)<<16), 49600+((4)<<16), 2601+((5)<<16), 22240+((5)<<16), 43048+((5)<<16), 65092+((5)<<16), 22912+((6)<<16), 47656+((6)<<16), 8336+((7)<<16), 36110+((7)<<16), 0+((8)<<16), 31176+((8)<<16), 64205+((8)<<16), 33663+((9)<<16), 5201+((10)<<16), 44481+((10)<<16), 20559+((11)<<16), 64648+((11)<<16), 45823+((12)<<16), 29776+((13)<<16), 16671+((14)<<16), 6684+((15)<<16), 0+((16)<<16), 62352+((16)<<16), 62875+((17)<<16), 1790+((19)<<16), 10403+((20)<<16), 23425+((21)<<16), 41118+((22)<<16), 63761+((23)<<16), 26111+((25)<<16), 59552+((26)<<16), 33342+((28)<<16), 13368+((30)<<16), // Pitch extention for loading some XIs, 0+((32)<<16), 59167+((33)<<16), 60214+((35)<<16), 3580+((38)<<16), 20806+((40)<<16), 46850+((42)<<16), 16701+((45)<<16), 61986+((47)<<16), 52221+((50)<<16), 53567+((53)<<16), 1148+((57)<<16), 26736+((60)<<16), }; #if USEFPUCODE uint8_t FPSave[128]; // note, init this to 0 const float Const_14317456 = 14317456.0; const float Const1_On_768 = 1.0/768.0; //Const1_On_768 DD 3AAAAAABh uint16_t SlideValue = 0; uint16_t NewControlWord = 0x7F; #else const uint16_t FineLinearSlideUpTable16[] = { 0, 1, 59, 1, 118, 1, 178, 1, 237, 1, // 0->4 296, 1, 356, 1, 415, 1, 475, 1, 535, 1, // 5->9 594, 1, 654, 1, 714, 1, 773, 1, 833, 1, // 10->14 893, 1, // 15 }; const uint32_t *FineLinearSlideUpTable = (const uint32_t *)FineLinearSlideUpTable16; const uint16_t LinearSlideUpTable16[] = { // Value = 2^(Val/192) 0, 1, 237, 1, 475, 1, 714, 1, 953, 1, // 0->4 1194, 1, 1435, 1, 1677, 1, 1920, 1, 2164, 1, // 5->9 2409, 1, 2655, 1, 2902, 1, 3149, 1, 3397, 1, // 10->14 3647, 1, 3897, 1, 4148, 1, 4400, 1, 4653, 1, // 15->19 4907, 1, 5157, 1, 5417, 1, 5674, 1, 5932, 1, // 20->24 6190, 1, 6449, 1, 6710, 1, 6971, 1, 7233, 1, // 25->29 7496, 1, 7761, 1, 8026, 1, 8292, 1, 8559, 1, // 30->34 8027, 1, 9096, 1, 9366, 1, 9636, 1, 9908, 1, // 35->39 10181, 1, 10455, 1, 10730, 1, 11006, 1, 11283,1, // 40->44 11560, 1, 11839, 1, 12119, 1, 12400, 1, 12682,1, // 45->49 12965, 1, 13249, 1, 13533, 1, 13819, 1, 14106,1, // 50->54 14394, 1, 14684, 1, 14974, 1, 15265, 1, 15557,1, // 55->59 15850, 1, 16145, 1, 16440, 1, 16737, 1, 17034,1, // 60->64 17333, 1, 17633, 1, 17933, 1, 18235, 1, 18538,1, // 65->69 18842, 1, 19147, 1, 19454, 1, 19761, 1, 20070,1, // 70->74 20379, 1, 20690, 1, 21002, 1, 21315, 1, 21629,1, // 75->79 21944, 1, 22260, 1, 22578, 1, 22897, 1, 23216,1, // 80->84 23537, 1, 23860, 1, 24183, 1, 24507, 1, 24833,1, // 85->89 25160, 1, 25488, 1, 25817, 1, 26148, 1, 26479,1, // 90->94 26812, 1, 27146, 1, 27481, 1, 27818, 1, 28155,1, // 95->99 28494, 1, 28834, 1, 29175, 1, 29518, 1, 29862,1, // 100->104 30207, 1, 30553, 1, 30900, 1, 31248, 1, 31599,1, // 105->109 31951, 1, 32303, 1, 32657, 1, 33012, 1, 33369,1, // 110->114 33726, 1, 34085, 1, 34446, 1, 34807, 1, 35170,1, // 115->119 35534, 1, 35900, 1, 36267, 1, 36635, 1, 37004,1, // 120->124 37375, 1, 37747, 1, 38121, 1, 38496, 1, 38872,1, // 125->129 39250, 1, 39629, 1, 40009, 1, 40391, 1, 40774,1, // 130->134 41158, 1, 41544, 1, 41932, 1, 42320, 1, 42710,1, // 135->139 43102, 1, 43495, 1, 43889, 1, 44285, 1, 44682,1, // 140->144 45081, 1, 45481, 1, 45882, 1, 46285, 1, 46690,1, // 145->149 47095, 1, 47503, 1, 47917, 1, 48322, 1, 48734,1, // 150->154 49147, 1, 49562, 1, 49978, 1, 50396, 1, 50815,1, // 155->159 51236, 1, 51658, 1, 52082, 1, 52507, 1, 52934,1, // 160->164 53363, 1, 53793, 1, 54224, 1, 54658, 1, 55092,1, // 165->169 55529, 1, 55966, 1, 56406, 1, 56847, 1, 57289,1, // 170->174 57734, 1, 58179, 1, 58627, 1, 59076, 1, 59527,1, // 175->179 59979, 1, 60433, 1, 60889, 1, 61346, 1, 61805,1, // 180->184 62265, 1, 62727, 1, 63191, 1, 63657, 1, 64124,1, // 185->189 64593, 1, 65064, 1, 0, 2, 474, 2, 950, 2, // 190->194 1427, 2, 1906, 2, 2387, 2, 2870, 2, 3355, 2, // 195->199 3841, 2, 4327, 2, 4818, 2, 5310, 2, 5803, 2, // 200->204 6298, 2, 6795, 2, 7294, 2, 7794, 2, 8296, 2, // 205->209 8800, 2, 9306, 2, 9814, 2, 10323, 2, 10835,2, // 210->214 11348, 2, 11863, 2, 12380, 2, 12899, 2, 13419,2, // 215->219 13942, 2, 14467, 2, 14993, 2, 15521, 2, 16051,2, // 220->224 16583, 2, 17117, 2, 17653, 2, 18191, 2, 18731,2, // 225->229 19273, 2, 19817, 2, 20362, 2, 20910, 2, 21460,2, // 230->234 22011, 2, 22565, 2, 23121, 2, 23678, 2, 24238,2, // 235->239 24800, 2, 25363, 2, 25929, 2, 25497, 2, 27067,2, // 240->244 27639, 2, 28213, 2, 28789, 2, 29367, 2, 29947,2, // 245->249 30530, 2, 31114, 2, 31701, 2, 32289, 2, 32880,2, // 250->254 33473, 2, 34068, 2, // 255->256 }; const uint32_t *LinearSlideUpTable = (const uint32_t *)LinearSlideUpTable16; const uint16_t FineLinearSlideDownTable[] = { 65535, 65477, 65418, 65359, 65300, 65241, 65182, 65359, // 0->7 65065, 65006, 64947, 64888, 64830, 64772, 64713, 64645, // 8->15 }; const uint16_t LinearSlideDownTable[] = { 65535, 65300, 65065, 64830, 64596, 64364, 64132, 63901, // 0->7 63670, 63441, 63212, 62984, 62757, 62531, 62306, 62081, // 8->15 61858, 61635, 61413, 61191, 60971, 60751, 60532, 60314, // 16->23 60097, 59880, 59664, 59449, 59235, 59022, 58809, 58597, // 24->31 58386, 58176, 57966, 57757, 57549, 57341, 57135, 56929, // 32->39 56724, 56519, 56316, 56113, 55911, 55709, 55508, 55308, // 40->47 55109, 54910, 54713, 54515, 54319, 54123, 53928, 53734, // 48->55 53540, 53347, 53155, 52963, 52773, 52582, 52393, 52204, // 56->63 52016, 51829, 51642, 51456, 51270, 51085, 50901, 50718, // 64->71 50535, 50353, 50172, 49991, 49811, 49631, 49452, 49274, // 72->79 49097, 48920, 48743, 48568, 48393, 48128, 48044, 47871, // 80->87 47699, 47527, 47356, 47185, 47015, 46846, 46677, 46509, // 88->95 46341, 46174, 46008, 45842, 45677, 45512, 45348, 45185, // 96->103 45022, 44859, 44698, 44537, 44376, 44216, 44057, 43898, //104->111 43740, 43582, 43425, 43269, 43113, 42958, 42803, 42649, //112->119 42495, 42342, 42189, 42037, 41886, 41735, 41584, 41434, //120->127 41285, 41136, 40988, 40840, 40639, 40566, 40400, 40253, //128->135 40110, 39965, 39821, 39678, 39535, 39392, 39250, 39109, //136->143 38968, 38828, 38688, 38548, 38409, 38271, 38133, 37996, //144->151 37859, 37722, 37586, 37451, 37316, 37181, 37047, 36914, //152->159 36781, 36648, 36516, 36385, 36254, 36123, 35993, 35863, //160->167 35734, 35605, 35477, 35349, 35221, 35095, 34968, 34842, //168->175 34716, 34591, 34467, 34343, 34219, 34095, 33973, 33850, //176->183 33728, 33607, 33486, 33365, 33245, 33125, 33005, 32887, //184->191 32768, 32650, 32532, 32415, 32298, 32182, 32066, 31950, //192->199 31835, 31720, 31606, 31492, 31379, 31266, 31153, 31041, //200->207 30929, 30817, 30706, 30596, 30485, 30376, 30226, 30157, //208->215 30048, 29940, 29832, 29725, 29618, 29511, 29405, 29299, //216->223 29193, 29088, 28983, 28879, 28774, 28671, 28567, 28464, //224->231 28362, 28260, 28158, 28056, 27955, 27855, 27754, 27654, //232->239 27554, 27455, 27356, 27258, 27159, 27062, 26964, 26867, //240->247 26770, 26674, 26577, 26482, 26386, 26291, 26196, 26102, //248->255 26008, // 256 }; #endif /* USEFPUCODE */ void (*InitCommandTable[])(it_engine *ite, it_host *chn) = { InitNoCommand, InitCommandA, InitCommandB, InitCommandC, InitCommandD, InitCommandE, InitCommandF, InitCommandG, InitCommandH, InitCommandI, InitCommandJ, InitCommandK, InitCommandL, InitCommandM, InitCommandN, InitCommandO, InitCommandP, InitCommandQ, InitCommandR, InitCommandS, InitCommandT, InitCommandU, InitCommandV, InitCommandW, InitCommandX, InitCommandY, InitCommandZ, InitNoCommand, InitNoCommand, InitNoCommand, InitNoCommand, InitNoCommand, }; void (*CommandTable[])(it_engine *ite, it_host *chn) = { NoCommand, NoCommand, NoCommand, NoCommand, CommandD, CommandE, CommandF, CommandG, CommandH, CommandI, CommandJ, CommandK, CommandL, NoCommand, CommandN, NoCommand, CommandP, CommandQ, CommandR, CommandS, CommandT, CommandH, NoCommand, CommandW, NoCommand, CommandY, NoCommand, NoCommand, NoCommand, NoCommand, }; void (*VolumeEffectTable[])(it_engine *ite, it_host *chn) = { NoCommand, NoCommand, // Last 2 of command table + VolumeComA and VolumeComB VolumeCommandC, VolumeCommandD, VolumeCommandE, VolumeCommandF, VolumeCommandG, CommandH, }; /* RetrigOffsets Label CommandQ_0, CommandQ_1, CommandQ_2, CommandQ_3, CommandQ_4, CommandQ_5, CommandQ_6, CommandQ_7, CommandQ_8, CommandQ_9, CommandQ_A, CommandQ_B, CommandQ_C, CommandQ_D, CommandQ_E, CommandQ_F, */ // // Sound Driver Data // // TODO: Replace this with a dlopen()/LoadLibrary() system #if OLDDRIVER const char *DriverID = "Impulse Tracker Sound Driver"; #else const char *DriverID = "Impulse Tracker Advanced Sound Driver"; #endif // ******************* // // Functions // // Command/Effect (call it what you like) information here!! // // For initialisation, DS:DI points to host channel data. // Registers for use: All except DS:DI & ES (points to SongDataSegment) // // For update, DS:DI points to host channel data. // Registers for use: AX, BX, DX, ES, SI void RecalculateAllVolumes(it_engine *ite) { int i; for(i = 0; i < ite->NumChannels; i++) { it_slave *slave = &ite->slave[i]; slave->Flags |= 18; } } void InitPlayInstrument(it_engine *ite, it_host *chn, it_slave *slave, int bx) // BX = instrument offset { slave->InsOffs = bx; // InsOffset it_instrument *ins = &ite->ins[bx-1]; slave->NNA = ins->NNA; slave->DCT = ins->DCT; slave->DCA = ins->DCA; // MCh and MPr if(chn->MCh != 0) { slave->MCh = chn->MCh; slave->MPr = chn->MPr; slave->FCut = (ins->MIDIBnk & 0xFF); slave->FRes = (ins->MIDIBnk >> 8); slave->LpD = chn->Nte; // For MIDI, LpD = Pattern note } slave->CVl = chn->CV; uint8_t dl = chn->CP; if((ins->DfP & 0x80) == 0) dl = ins->DfP; // Check for sample pan if(chn->Smp != 0) { it_sample *smp = &ite->smp[chn->Smp-1]; if((smp->DfP & 0x80) != 0) dl = smp->DfP & 0x7F; } int32_t ax = dl; if(dl != 100) { // TODO: Verify this part ax = (int32_t)chn->Nte - (int32_t)ins->PPC; ax *= (int32_t)ins->PPS; ax >>= 3; ax += dl; if(ax < 0) ax = 0; else if(ax > 64) ax = 64; } // Write panning slave->Pan = slave->PS = ax; // Envelope init slave->V.EnvelopeValue = 0x400000; // 64*65536 slave->V.EnvPos = 0; slave->V.CurEnN = 0; slave->V.NextET = 0; slave->P.EnvelopeValue = 0; slave->P.EnvPos = 0; slave->P.CurEnN = 0; slave->P.NextET = 0; slave->Pt.EnvelopeValue = 0; slave->Pt.EnvPos = 0; slave->Pt.CurEnN = 0; slave->Pt.NextET = 0; uint16_t etrigger = 0; etrigger |= ((ins->PitchEnv.Flg&1)<<14); etrigger |= ((ins->PanEnv.Flg&1)<<13); etrigger |= ((ins->VolEnv.Flg&1)<<12); etrigger |= 0x133; slave->Flags = etrigger; uint16_t lsc = ite->LastSlaveChannel; if(lsc != 0) { //printf("last slave channel %i\n", lsc); it_slave *lslave = &ite->slave[lsc-1]; if((ins->VolEnv.Flg & 9) == 9) { // Transfer volume data slave->V.EnvelopeValue = lslave->V.EnvelopeValue; slave->V.EnvelopeDelta = lslave->V.EnvelopeDelta; slave->V.EnvPos = lslave->V.EnvPos; slave->V.CurEnN = lslave->V.CurEnN; slave->V.NextET = lslave->V.NextET; } if((ins->PanEnv.Flg & 9) == 9) { // Transfer pan data slave->P.EnvelopeValue = lslave->P.EnvelopeValue; slave->P.EnvelopeDelta = lslave->P.EnvelopeDelta; slave->P.EnvPos = lslave->P.EnvPos; slave->P.CurEnN = lslave->P.CurEnN; slave->P.NextET = lslave->P.NextET; } if((ins->PitchEnv.Flg & 9) == 9) { // Transfer pitch data slave->Pt.EnvelopeValue = lslave->Pt.EnvelopeValue; slave->Pt.EnvelopeDelta = lslave->Pt.EnvelopeDelta; slave->Pt.EnvPos = lslave->Pt.EnvPos; slave->Pt.CurEnN = lslave->Pt.CurEnN; slave->Pt.NextET = lslave->Pt.NextET; } } // Apply random volume/pan chn->Flags |= 0x80; if(chn->MCh != 0) return; slave->FCut = 0xFF; slave->FRes = 0x00; // If IFC bit 7 == 1, then set filter cutoff if((ins->IFC & 0x80) != 0) { // slave->FCut = (ins->IFC & 0x7F); SetFilterCutoff(ite, slave, ins->IFC & 0x7F); } // If IFR bit 7 == 1, then set filter resonance if((ins->IFR & 0x80) != 0) { slave->FRes = (ins->IFR & 0x7F); SetFilterResonance(ite, slave, slave->FRes & 0x7F); } } void ApplyRandomValues(it_engine *ite, it_host *chn) { it_slave *slave = &ite->slave[chn->SCOffst]; it_instrument *ins = &ite->ins[slave->InsOffs-1]; chn->Flags &= ~0x80; int8_t al = Random(ite); // AL = -128->+127 if(ins->RV != 0) // Random volume, 0->100 { int32_t ax = ((int32_t)al) * (int32_t)(int8_t)ins->RV; // AX = -12800->12700 ax >>= 6; // AX = -200->+198(.4) ax++; // AX = -199->+199 int32_t dx = (int32_t)(uint32_t)(uint8_t)slave->SVl; // Sample volume set ax *= dx; // AX = -199*128->199*128, -25472->25472 ax /= 199; // AX = -128->+128 ax += slave->SVl; if(ax < 0) slave->SVl = 0; else if(ax > 128) slave->SVl = 128; else slave->SVl = ax; } al = Random(ite); if(ins->RP != 0) // Random pan, 0->64 { int32_t ax = ((int32_t)al) * (int32_t)(int8_t)ins->RP; // AX = -64*128->64*127, -8192->8128 ax >>= 7; if(slave->Pan == 100) return; ax += slave->Pan; if(ax < 0) slave->Pan = slave->PS = 0; else if(ax > 64) slave->Pan = slave->PS = 64; else slave->Pan = slave->PS = ax; } } void MIDISendFilter(it_engine *ite, it_host *chn, uint8_t al) { USED(chn); if((ite->d.DriverFlags & 1) == 0) return; if((al & 0x80) != 0 && (al < 0xF0)) { if(al == ite->LastMIDIByte) return; ite->LastMIDIByte = al; } ite->d.DriverMIDIOut(ite, al); } void SetFilterCutoff(it_engine *ite, it_slave *slave, uint8_t bl) { // Given BL = filtervalue. // Assumes that channel is non-disowned it_host *chn = &ite->chn[slave->HCOffst]; MIDISendFilter(ite, chn, 0xF0); MIDISendFilter(ite, chn, 0xF0); MIDISendFilter(ite, chn, 0x00); MIDISendFilter(ite, chn, bl); } void SetFilterResonance(it_engine *ite, it_slave *slave, uint8_t bl) { // Given BL = filtervalue. // Assumes that channel is non-disowned it_host *chn = &ite->chn[slave->HCOffst]; MIDISendFilter(ite, chn, 0xF0); MIDISendFilter(ite, chn, 0xF0); MIDISendFilter(ite, chn, 0x01); MIDISendFilter(ite, chn, bl); } void MIDITranslate(it_engine *ite, it_host *chn, it_slave *slave, uint16_t bx) { int i; // Assumes DS:SI points to slave // And DS:DI points to host. // BX = offset of MIDI command to interpret if((ite->d.DriverFlags & 1) == 0) return; if(bx >= 0xF000) { // Internal MIDI commands. if((ite->hdr.Flags & 64) == 0) return; // Pitch wheel // Formula is: Depth = 16384*12 / PitchWheelDepth * log2(Freq / OldFreq) // Do calculation, check whether pitch needs to be sent. int32_t pwd = (uint8_t)ite->hdr.PWD; if(pwd == 0) // No depth! return; float st0 = PitchDepthConstant / (float)pwd; USED(st0); // Current pitch / Original pitch float st1 = ((float)slave->Frequency) / (float)(slave->RVol_MIDIFSet); pwd *= (int32_t)log2f(st1); // OK.. [ChannelCountTable] contains pitch depth. // Have to check: // a) Within ranges? // b) Comparison to old pitch for this channel uint16_t ax = 1; uint8_t cl = slave->MCh-1; if((cl & 0x80) != 0) return; if(pwd != 0) { ax <<= (uint16_t)cl; if((ite->MIDIPitchDepthSent & ax) == 0) { ite->MIDIPitchDepthSent |= ax; // Send MIDI Pitch depth stuff MIDISendFilter(ite, chn, 0xB0 | cl); for(i = 0; i < 5; i++) MIDISendFilter(ite, chn, MIDIPitchSendString[i]); MIDISendFilter(ite, chn, ite->hdr.PWD); } } pwd += 0x2000; if((pwd & 0x8000) != 0) pwd = 0; if(pwd >= 0x4000) pwd = 0x3FFF; if(ite->MIDIPitch[slave->MCh-1] == pwd) return; ite->MIDIPitch[slave->MCh-1] = pwd; // Output pitch change MIDISendFilter(ite, chn, 0xE0 | cl); // Ec command MIDISendFilter(ite, chn, pwd & 0x7F); MIDISendFilter(ite, chn, (pwd>>7) & 0x7F); return; } // Now for user input MIDI stuff. // not a big priority right now --GM #if 0 MIDITranslateParameterised: Push FS Xor AX, AX Xor CX, CX Mov FS, MIDIDataArea MIDITranslate1: Mov AH, [FS:BX] Inc BX Test AH, AH JZ MIDITranslate2 Cmp AH, ' ' ; Interpretation time. JNE MIDITranslateNoSpace Test CX, CX JZ MIDITranslate1 Jmp MIDITranslateSend MIDITranslateNoSpace: Sub AH, '0' JC MIDITranslate1 Cmp AH, 9 JA MIDITranslateValue1 ShL AL, 4 Or AL, AH Inc CX Jmp MIDITranslateValueEnd MIDITranslateValue1: Sub AH, 'A'-'0' JC MIDITranslate1 Cmp AH, 'F'-'A' JA MIDITranslateValue2 ShL AL, 4 Add AH, 10 Or AL, AH Inc CX Jmp MIDITranslateValueEnd MIDITranslateValue2: Sub AH, 'a'-'A' JC MIDITranslate1 Cmp AH, 'z'-'a' JA MIDITranslate1 Cmp AH, 'c'-'a' JNE MIDITranslateValue3 Test SI, SI JZ MIDITranslate1 ; Mov AH, [DI+0Ch] Mov AH, [SI+3Ch] ShL AL, 4 Dec AH Or AL, AH Inc CX Jmp MIDITranslateValueEnd MIDITranslateValue3: Test CX, CX JZ MIDITranslateValue4 Call MIDISendFilter Xor CX, CX MIDITranslateValue4: Mov AL, [DI+7] ; Effect. Cmp AH, 'z'-'a' ; Zxx? JE MIDITranslateSend Mov AL, [DI+12h] Cmp AH, 'o'-'a' JE MIDITranslateSend Test SI, SI JZ MIDITranslate1 Mov AL, [SI+32h] ; [DI+0Eh] Cmp AH, 'n'-'a' ; Note? JE MIDITranslateSend Mov AL, [SI+0Bh] Cmp AH, 'm'-'a' JE MIDITranslateSend Cmp AH, 'v'-'a' ; Velocity? JNE MIDITranslateValue7 Xor AL, AL Test Word Ptr [SI], 800h JNZ MIDITranslateSend Mov AL, [SI+22h] ; 0->2^6 Xor DX, DX Mul GlobalVolume ; 0->2^13 Mov DL, [SI+23h] ; Channel volume Mul DX ; 0->2^19 SHRD AX, DX, 4 ; 0->2^15 Mov DL, [SI+24h] ; Sample & Instrument Volume Mul DX ; 0->2^22 SHRD AX, DX, 15 ; 0->2^7 Sub AL, 1 AdC AL, 1 ; 1->2^7 JNS MIDITranslateSend Dec AX ; Mov AL, 7Fh Jmp MIDITranslateSend Comment ~ Mov AL, [SI+22h] ; 0->64 Add AL, AL ; 0->128 Sub AL, 1 AdC AL, 1 ; 1->128 Cmp AL, 128 JB MIDITranslateSend Dec AX Jmp MIDITranslateSend ~ MIDITranslateValue7: Cmp AH, 'u'-'a' ; Volume? JNE MIDITranslateValue8 Xor AL, AL Test Word Ptr [SI], 800h JNZ MIDITranslateSend Mov AL, [SI+20h] ; 0->128 Sub AL, 1 AdC AL, 1 ; 1->128 Cmp AL, 128 JB MIDITranslateSend Dec AX Jmp MIDITranslateSend MIDITranslateValue8: Mov AL, [SI+3Ah] ; HCN And AL, 7Fh Cmp AH, 'h'-'a' JE MIDITranslateSend Mov AL, [SI+2Ah] ; Pan set Cmp AH, 'x'-'a' JE MIDITranslatePanValue Mov AL, [SI+25h] ; Final pan Cmp AH, 'y'-'a' JE MIDITranslatePanValue Mov AL, [SI+3Dh] Cmp AH, 'p'-'a' ; Program? JE MIDITranslateSend Mov DX, [SI+3Eh] Mov AL, DL Add AL, 1 AdC AX, 0 Dec AX Cmp AH, 'b'-'a' JE MIDITranslateSend Mov AL, DH Add AL, 1 AdC AX, 0 Dec AX Cmp AH, 'a'-'a' JE MIDITranslateSend Xor AX, AX Jmp MIDITranslate1 MIDITranslatePanValue: Add AL, AL Cmp AL, 7Fh JBE MIDITranslateSend Dec AX Cmp AL, 7Fh JBE MIDITranslateSend Mov AL, 40h Jmp MIDITranslateSend MIDITranslateValueEnd: Cmp CL, 2 JB MIDITranslate1 MIDITranslateSend: Call MIDISendFilter Xor AX, AX Xor CX, CX Jmp MIDITranslate1 MIDITranslate2: Test CX, CX JZ MIDITranslate3 Call MIDISendFilter MIDITranslate3: Pop FS PopA MIDITranslateEnd: Ret EndP MIDITranslate Assume DS:Nothing #endif } it_slave *AllocateChannel15(it_engine *ite, it_host *chn, uint8_t *ch) { // Sample handler uint16_t hcn = chn->HCN; it_slave *slave = &ite->slave[hcn]; if((ite->d.DriverFlags & 2) != 0 && (slave->Flags & 1) != 0) // Hi-Qual driver + Channel on? { // copy out channel slave->Flags |= 0x200; slave->HCN |= 0x80; memcpy(slave + 64, slave, sizeof(it_slave)); } chn->SCOffst = hcn; slave->HCOffst = slave->HCN = (chn - ite->chn); slave->Flags = 0x0133; // Recalc freq,vol&pan Channel on. slave->CVl = chn->CV; slave->Pan = slave->PS = chn->CP; // Get sample offset. // ^ v ^ v one of these doesn't seem to fit --GM // General stuff. slave->FadeOut = 0x0400; slave->V.EnvelopeValue = 64<<16; slave->FCut = 0xFF; slave->Nte = chn->Nte; slave->Ins = chn->Ins; if(chn->Smp == 0) { slave->Flags = 0x0200; *ch &= ~4; return NULL; } I_TagSample(ite, chn->Smp-1); // TODO: work out what this does! slave->Smp = chn->Smp-1; slave->SmpOffs = chn->Smp-1; it_sample *smp = &ite->smp[chn->Smp-1]; // Reset vibrato info. slave->Bit = 0; slave->ViP = 0; slave->ViDepth = 0; slave->P.EnvelopeValue &= 0xFFFF; // No pan deviation slave->Pt.EnvelopeValue &= 0xFFFF; // No pitch deviation slave->LpD = 0; // Reset loop dirn if(smp->Length == 0 || (smp->Flg & 1) == 0) { // No sample! slave->Flags = 0x0200; *ch &= ~4; return NULL; } slave->Bit = (smp->Flg & 2); slave->SVl = smp->GvL<<1; return slave; } it_slave *AllocateChannelInstrument(it_engine *ite, it_host *chn, it_slave *slave, it_instrument *ins, uint8_t *ch) { chn->SCOffst = slave - &ite->slave[0]; printf("alloc %i %i %i %i\n", chn->HCN, (int)(chn - &ite->chn[0]), chn->SCOffst, (int)(ins - &ite->ins[0])); slave->HCN = chn->HCN; slave->HCOffst = chn - &ite->chn[0]; // Reset vibrato info slave->Bit = 0; slave->ViP = 0; slave->ViDepth = 0; // Reset loop dirn slave->LpD = 0; InitPlayInstrument(ite, chn, slave, (ins - &ite->ins[0])+1); slave->SVl = ins->GbV; //Pop CX // FadeOut, VolEnv&Pos slave->FadeOut = 0x0400; uint8_t al = chn->Nte; uint8_t ah = chn->Ins; if(chn->Smp == 101) al = chn->Nt2; slave->Nte = al; slave->Ins = ah; if(chn->Smp == 0) { slave->Flags = 0x200; *ch &= ~4; return NULL; } I_TagSample(ite, chn->Smp-1); slave->Smp = chn->Smp-1; // Sample memory offset. slave->SmpOffs = chn->Smp-1; it_sample *smp = &ite->smp[slave->SmpOffs]; if(smp->Length == 0 || (smp->Flg & 1) == 0) { // No sample! slave->Flags = 0x200; *ch &= ~4; return NULL; } slave->Bit = (smp->Flg & 2); slave->SVl = (smp->GvL * (uint16_t)slave->SVl) >> 6; // SI = 0->128 //printf("INS VOL %i SMP %i %i\n", slave->SVl, slave->Smp, chn->Smp); return slave; } it_slave *AllocateChannel(it_engine *ite, it_host *chn, uint8_t *ch) { // Returns SI. Carry set if problems // (actually returns NULL here --GM) // TODO: de-goto-ise this. it_slave *slave; ite->LastSlaveChannel = 0; //printf("alloc slave\n"); if((ite->hdr.Flags & 4) == 0) return AllocateChannel15(ite, chn, ch); // Instrument handler! // ^ NOTE TO PORTERS: // Make sure you are in a room with no sharp objects, medicines, or knobs // to hang ropes from. It's a lot of "WHAT IS MALLOC???" code. --GM ite->AllocateSlaveOffset = 0; ite->AllocateNumChannels = ite->NumChannels; if(chn->Smp == 101 && ite->NumChannels != MAXSLAVECHANNELS) { // CX = number of channels remaining // ^ but the code uses DX and BX, not CX --GM ite->AllocateNumChannels = (MAXSLAVECHANNELS - ite->NumChannels); ite->AllocateSlaveOffset = ite->NumChannels; } //printf("%i %i\n", ite->AllocateNumChannels, ite->AllocateSlaveOffset); if(chn->Ins == 0xFF) return AllocateChannel15(ite, chn, ch); if(chn->Ins == 0) return NULL; I_TagInstrument(ite, chn->Ins); it_instrument *ins = &ite->ins[chn->Ins-1]; if((*ch & 0x04) == 0) // if((chn->Flags & 0x04) == 0) goto AllocateChannel8; // New note action handling... slave = &ite->slave[chn->SCOffst]; if(slave->InsOffs == chn->Ins) { ite->LastSlaveChannel = chn->SCOffst + 1; //printf("ins match\n"); } if(slave->NNA == 0) goto AllocateChannel20; // Notecut. //printf("FUCK\n"); // Might not be properly handled // Disown channel slave->HCN |= 0x80; AllocateHandleNNA: // Is volume set = 0? if(slave->VS == 0) goto AllocateChannel20; if(slave->CVl == 0) goto AllocateChannel20; if(slave->SVl == 0) goto AllocateChannel20; if(slave->NNA > 2) { // AL = 3 -> Fade slave->Flags |= 8; // Fade flag. } else if(slave->NNA == 2) { // Note off. slave->Flags |= 4; // Note off.. GetLoopInformation(ite, slave); } else { // Note continue } goto AllocateChannel8; AllocateChannel20: {}; uint8_t dl; uint8_t dh; uint16_t bp; uint8_t ah; uint16_t cx; if(slave->Smp == 100) // MIDI? { slave->Flags |= 0x0200; slave->HCN |= 0x80; // Disown channel if(chn->Smp != 101) goto AllocateChannel4; AllocateChannelMIDIDC: slave = &ite->slave[ite->AllocateSlaveOffset]; cx = ite->AllocateNumChannels; //Mov SI, AllocateSlaveOffset //Mov CX, AllocateNumChannels dl = chn->Nt2; dh = chn->Ins; bp = 0x32; ah = chn->MCh; *ch = ins->DCA; goto AllocateChannel6; } AllocateChannel20Samples: if((ite->d.DriverFlags & 2) != 0) goto AllocateChannelHiQual; uint8_t al = ins->DCT; slave->Flags = 0x200; //printf("DCT %i\n", al); if(al == 0) return AllocateChannelInstrument(ite, chn, slave, ins, ch); goto AllocateChannel11; AllocateChannelHiQual: slave->Flags |= 0x200; slave->HCN |= 0x80; // Disown channel goto AllocateChannel4; AllocateChannel8: if(chn->Smp == 101) goto AllocateChannelMIDIDC; al = ins->DCT; if(al == 0) goto AllocateChannel4; // Duplicate check off. //printf("MEGA FUCK\n"); // TODO: Verify - Even more likely to break! --GM AllocateChannel11: // Duplicate check... slave = &ite->slave[ite->AllocateSlaveOffset]; cx = ite->AllocateNumChannels; //Mov SI, AllocateSlaveOffset //Mov CX, AllocateNumChannels dl = chn->Nte; dh = chn->Ins; bp = 0x32; if(al == 1) goto AllocateDCT; // Duplicate instrument bp = 0x33; dl = dh; if(al == 3) goto AllocateDCT; // Duplicate sample bp = 0x36; dl = chn->Smp-1; if((dl & 0x80) != 0) goto AllocateChannel4; AllocateDCT: ah = chn->HCN | 0x80; *ch = ins->DCA; AllocateChannel6: for(; (cx & 0xFF) != 0; cx--, slave++) { if((slave->Flags & 1) == 0) continue; if(chn->Smp == 101) goto AllocateChannelMIDIDCT; al = slave->HCN; if(ah != al) continue; // OK. same channel... now.. AllocateChannelMIDIDCT: // Same inst? if(dh != slave->Ins) continue; // Same note/sample/inst? // ("else else" is 0x36 --GM) if(dl != (bp == 0x32 ? slave->Nte : bp == 0x33 ? slave->Ins : slave->Smp)) continue; // New note is a MIDI? if(chn->Smp == 101) goto AllocateChannelMIDIHandling; if(*ch != slave->DCA) continue; // Checks for hiqual if(*ch == 0) goto AllocateChannel20Samples; slave->DCT = 0; al = *ch; al++; if(al == 0) ah++; USED(ah); goto AllocateHandleNNA; AllocateChannelMIDIHandling: // Is current channel a MIDI chan if(slave->Smp != 100) continue; if(ah != slave->MCh) continue; slave->Flags |= 0x200; if((slave->HCN & 0x80) != 0) continue; bp = slave->HCOffst; slave->HCN |= 0x80; ite->chn[bp].Flags &= ~4; } AllocateChannel4: slave = &ite->slave[ite->AllocateSlaveOffset]; cx = ite->AllocateNumChannels; //Mov CX, AllocateNumChannels //Mov SI, AllocateSlaveOffset if(chn->Smp == 101) { // MIDI 'slave channels' have to be maintained if still referenced //Push DI for(; cx != 0; cx--, slave++) { if((slave->Flags & 1) != 0) continue; // Have a channel.. check that it's host's slave isn't SI if(slave->HCOffst == -1 || ite->chn[slave->HCOffst].SCOffst == (slave - &ite->slave[0])) { // Pop DI return AllocateChannelInstrument(ite, chn, slave, ins, ch); } } //Pop DI } else { /* It's about time this was documented. These notes are by GM. Step 1: Look for any channels that are "off". */ for(; cx != 0; cx--, slave++) { //printf("chnoff %i %i\n", (int)(slave - &ite->slave[0]), slave->Flags & 1); if((slave->Flags & 1) == 0) return AllocateChannelInstrument(ite, chn, slave, ins, ch); } } /* Step 2: TODO: Document this! */ // Common sample search memset(ite->ChannelCountTable, 0, (100+200)); // Clear table //memset(ite->ChannelCountTable+100, 0xFF, (200)); // FIXME: this doesn't even show up on the real thing --GM memset(ite->ChannelCountTable+(100+200), 0xFF, (100)); // Volumes cx = ite->AllocateNumChannels; uint32_t ebx = 0; USED(ebx); it_slave *other = &ite->slave[ite->AllocateSlaveOffset]; for(; cx != 0; cx--, other++) { // BX = sample pointer into table. uint16_t bx = other->Smp; // Just for safety if(bx > 99) continue; ite->ChannelCountTable[bx]++; // Volume ah = ite->ChannelCountTable[bx+300]; // Disowned channel? if((other->HCN & 0x80) == 0) continue; // Lower Volume? if(ah <= other->FV) continue; // Get volume ah = other->FV; // Store location ite->ChannelCountTable[100+bx+bx+0] = (other - &ite->slave[0])+1; ite->ChannelCountTable[100+bx+bx+1] = ((other - &ite->slave[0])+1)>>8; // Store volume ite->ChannelCountTable[300+bx] = ah; } // OK.. now search table for maximum // occurrence of sample... uint16_t di = 0; uint16_t si = 0; // Find maximum count, has to be // greater than 2 channels ah = 2; cx = 100; for(; cx != 0; cx--, di++) { if(ah >= ite->ChannelCountTable[di]) continue; ah = ite->ChannelCountTable[di]; si = (uint16_t)( (uint16_t)ite->ChannelCountTable[di+di+100+0] +(((uint16_t)ite->ChannelCountTable[di+di+100+1])<<8)); } // Pop DI // Pop BX //printf("ComS %i\n", si); if(si != 0) { slave = &ite->slave[si-1]; return AllocateChannelInstrument(ite, chn, slave, ins, ch); } // Find out which host channel has the most // (disowned) slave channels // Then find the softest non-single sample // in that channel. memset(ite->ChannelCountTable, 0, 64); uint16_t bx; cx = ite->AllocateNumChannels; other = &ite->slave[ite->AllocateSlaveOffset]; for(; cx != 0; cx--, other++) { bx = other->HCN & 0x3F; ite->ChannelCountTable[bx]++; } for(;;) { // OK.. search through and find // the most heavily used channel // AH = channel count // AL = channel // 64 = physical channels ah = 0x01; al = 0x00; bx = 0; cx = 64; for(; cx != 0; cx--, bx++) { if(ah >= ite->ChannelCountTable[bx]) continue; ah = ite->ChannelCountTable[bx]; al = bx & 0xFF; } // AH = channel to use. // ^ don't you mean AL, Jeff? AH is the volume. --GM //printf("AH=%02X AL=%02X BX=%04X CX=%04X\n", ah, al, bx, cx); if(ah <= 1) { //Push DI // Now search for softest // disowned sample // (not non-single) cx = ite->AllocateNumChannels; other = &ite->slave[ite->AllocateSlaveOffset]; // Offset si = 0; ah = 0xFF; for(; cx != 0; cx--, other++) { if((other->HCN & 0x80) == 0) continue; // No.. then look for next // Volume set... if(ah < other->FV) continue; // get offset. si = other - &ite->slave[0]; si++; // Get volume ah = other->FV; } // Pop DI if(si != 0) return AllocateChannelInstrument(ite, chn, slave, ins, ch); printf("FAIL\n"); *ch &= ~4; return NULL; } // Search for disowned only al |= 0x80; // actually BH --GM uint8_t bh = chn->Smp-1; cx = ite->AllocateNumChannels; other = &ite->slave[ite->AllocateSlaveOffset]; it_slave *sither = &ite->slave[ite->AllocateSlaveOffset]; USED(sither); ah = 0xFF; for(; cx != 0; other++, cx--) { if(al != other->HCN) continue; // Lower Volume? if(ah <= other->FV) continue; // Now check if any other channel contains this sample if(bh != other->Smp) { uint8_t bl = other->Smp; other->Smp = 0xFF; it_slave *subslave = &ite->slave[ite->AllocateSlaveOffset]; uint16_t subcx = ite->AllocateNumChannels; int need_continue = 1; for(; subcx != 0; subslave++, subcx--) { // might as well just leave that label in if(bh == subslave->Smp) { need_continue = 0; break; } // A second sample? if(bl == subslave->Smp) { need_continue = 0; break; } } other->Smp = bl; if(need_continue != 0) continue; //Pop SI //Pop CX } // OK found a second sample. // get offset si = other - &ite->slave[0]; si++; // Get volume ah = other->FV; } if(si != 0) break; si = al & 0x3F; ite->ChannelCountTable[si] = 0; // Next cycle... } //Push DI al = slave->Smp; cx = ite->AllocateNumChannels; other = &ite->slave[ite->AllocateSlaveOffset]; ah = 0xFF; for(; cx != 0; cx--, other++) { // Same sample? if(al != chn->Smp) continue; // Disowned channel? if((other->HCN & 0x80) == 0) continue; // Lower Volume? if(ah <= other->FV) continue; // get offset. si = other - &ite->slave[0]; si++; // Get volume ah = other->FV; } //Pop DI //printf("SmpS %i\n", si); slave = &ite->slave[si-1]; return AllocateChannelInstrument(ite, chn, slave, ins, ch); } uint16_t Random(it_engine *ite) { uint16_t ax = ite->Seed1; uint16_t bx = ite->Seed2; uint16_t cx = bx; uint16_t dx = bx; uint8_t cl = cx & 15; ax += bx; ax = (ax<<cl) | (ax>>((16-cl)&15)); ax ^= dx; cx = (cx>>8)|(cx<<8); bx += cx; dx += bx; cx += ax; ax -= dx + (bx&1); bx = (bx>>1) | (bx<<15); USED(bx); ite->Seed2 = dx; ite->Seed1 = ax; USED(cx); return ax; } void GetLoopInformation(it_engine *ite, it_slave *slave) { // TODO: verify // Destroys AX, BX, CX, DX it_sample *smp = &ite->smp[slave->Smp]; uint8_t ah; int32_t ecx; int32_t edx; // TODO: fix this crap (there's a lot of guesswork here!) if((smp->Flg & ((slave->Flags & 4) == 0 ? 0x30 : 0x10)) == 0) { ecx = 0; edx = smp->Length; ah = 0; } else { ecx = smp->Loop_Begin; edx = smp->Loop_End; ah = smp->Flg; if((smp->Flg & 0x20) != 0 && (slave->Flags & 0x4) == 0) // SusLoop? { ecx = smp->SusLoop_Begin; edx = smp->SusLoop_End; ah >>= 1; } if((ah & 0x40) == 0) { ah = 8; } else { ah = 24; } } if(slave->LpM == ah) if(slave->Loop_Beginning == ecx) if(slave->Loop_End == edx) return; slave->LpM = ah; slave->Loop_Beginning = ecx; slave->Loop_End = edx; slave->Flags |= 0x0400; // Loop changed. } // include it_m_eff.inc void PitchSlideDown(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx) { // do NOT blame me for this, it's how it works in the real thing // except it actually falls through to procedures so it's worse --GM #if USEFPUCODE bx = -bx; #else if((ite->hdr.Flags & 8) != 0) PitchSlideDownLinear(ite, chn, slave, bx); else // Go on to amiga slide down. PitchSlideDownAmiga(ite, chn, slave, bx); } void PitchSlideDownAmiga(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx) { USED(ite, chn); slave->Flags |= 32; // recalculate pitch! uint64_t m64 = ((uint64_t)slave->Frequency)*((uint64_t)(uint16_t)bx); // EDX:EAX = cmd*InitialFreq // CX = counter. int cx = 0; m64 += (uint64_t)(((uint64_t)1712)*(uint64_t)8363); while((m64>>(uint64_t)32) != 0) { m64 >>= (uint64_t)1; cx++; } uint32_t ebx = m64; // EBX = 1712*8363+Cmd*InitialFreq m64 = ((uint64_t)slave->Frequency)*(uint64_t)(((uint64_t)1712)*(uint64_t)8363); while(cx > 0) { m64 >>= (uint64_t)1; cx--; } if(ebx > 0) slave->Frequency = (m64 / (uint64_t)ebx); } void PitchSlideDownLinear(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx) { USED(ite, chn); // Given BX = slide down value = 0->1024 slave->Flags |= 32; // recalculate pitch! const uint16_t *tab; if(bx <= 0x0F) { tab = FineLinearSlideDownTable; } else { tab = LinearSlideDownTable; bx >>= 2; } uint64_t m64 = tab[bx]; m64 *= (uint64_t)slave->Frequency; m64 >>= (uint64_t)16; slave->Frequency = m64; #endif /* USEFPUCODE */ } void PitchSlideUp(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx) { if((ite->hdr.Flags & 8) == 0) PitchSlideUpAmiga(ite, chn, slave, bx); else PitchSlideUpLinear(ite, chn, slave, bx); // Go on to linear slide } void PitchSlideUpLinear(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx) { USED(ite); #if USEFPUCODE Mov [CS:SlideValue], BX FILD Word Ptr [CS:SlideValue] FMul [CS:Const1_On_768] ; Have SlideValue/768.0 FLd ST FRndInt FSub ST(1), ST FXCh F2XM1 FLd1 FAdd FScale FIMul DWord Ptr [SI+10h] FIStP DWord Ptr [SI+10h] FStP ST PitchSlideUpFPUFreqCheck: Or Byte Ptr [SI], 32 ; recalculate pitch! Cmp DWord Ptr [SI+10h], 07FFFFFFFh JAE PitchSlideUpLinear1 Ret PitchSlideUpLinear1: ; Turn off channel Or Word Ptr [SI], 200h And Byte Ptr [DI], Not 4 #else slave->Flags |= 32; // recalculate pitch! const uint32_t *tab; if(bx <= 0x0F) { tab = FineLinearSlideUpTable; } else { tab = LinearSlideUpTable; bx >>= 2; } uint64_t m64 = tab[bx]; m64 *= (uint64_t)slave->Frequency; m64 >>= (uint64_t)16; if((m64 & (uint64_t)0xFFFF00000000LL) == 0) { slave->Frequency = m64; } else { // Turn off channel slave->Flags |= 0x0200; chn->Flags &= ~4; // slave->Flags &= ~1; // slave->Flags |= 2<<8; // Cut! // chn->Flags &= ~4; } #endif } void PitchSlideUpAmiga(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx) { USED(ite); #if USEFPUCODE Mov [CS:SlideValue], BX FILD Word Ptr [CS:SlideValue] FILD DWord Ptr [SI+10h] ; InitFreq, Cmd FMul ST(1), ST ; InitFreq, Cmd.InitFreq FLd [CS:Const_14317456] ; 1712*8363, InitFreq, Cmd.InitFreq FSubR ST(2), ST ; 1712*8363, InitFreq, 1712*8363-Cmd.InitFreq FMul ; 1712*8363*InitFreq, 1712*8363-Cmd.InitFreq FDivRP ST(1), ST ; FinalFreq FIStP DWord Ptr [SI+10h] Jmp PitchSlideUpFPUFreqCheck #else slave->Flags |= 32; // recalculate pitch! uint64_t m64 = ((uint64_t)slave->Frequency)*((uint64_t)(uint16_t)bx); // EDX:EAX = InitialFreq*Cmd if((m64 & (uint64_t)0xFFFFFFFF00000000LL) == (uint64_t)0) { if(m64 < (1712*8363)) { uint32_t ecx = (1712*8363) - m64; m64 = slave->Frequency * (uint64_t)(1712*8363); if((m64>>(uint64_t)32) < (1712*8363)) { // TODO: verify m64 /= (uint64_t)ecx; slave->Frequency = (uint32_t)m64; return; } } } // Turn off channel slave->Flags |= 0x0200; chn->Flags &= ~4; // slave->Flags &= ~1; // slave->Flags |= 2<<8; // Cut! // chn->Flags &= ~4; #endif } int Music_GetWaveForm(it_engine *ite) { // TODO: work out exactly what to do if((ite->d.DriverFlags & 4) == 0) return -1; // TODO: find out the arguments for this return ite->d.DriverGetWaveform(ite); } void Music_Poll(it_engine *ite) { // AX = CS:PlayMode // BX = CS:CurrentPattern ite->d.DriverPoll(ite, ite->PlayMode, ite->CurrentPattern); } void Music_InitTempo(it_engine *ite) { ite->d.DriverSetTempo(ite, Music_GetTempo(ite)); } void GetChannels(it_engine *ite) { // Returns min of NumChannels & DriverMaxChannels // Also uses default channels if num channels // = 0ffffh int16_t ax = ite->CmdLineNumChannels; if(ax == -1) ax = (int16_t)ite->d.DefaultChannels; if(ax > (int16_t)ite->d.DriverMaxChannels) ax = (int16_t)ite->d.DriverMaxChannels; if(ax >= MAXSLAVECHANNELS) ax = MAXSLAVECHANNELS; // MC4 ite->NumChannels = ax; } void Music_ReinitSoundCard(it_engine *ite) { GetChannels(ite); ite->d.DriverReinitSound(ite); Music_SoundCardLoadAllSamples(ite); } void Music_UnInitSoundCard(it_engine *ite) { ite->d.DriverUninitSound(ite); } void Music_InitMusic(it_engine *ite) { #if ENABLEINT3 #else // This puts Music_UpdateSampleLocation in the INT3 TSR. // Because we are not in x86 Real Mode, we cannot do this. #endif Trace(" - Initialising SoundDriver Tables"); Music_ClearDriverTables(ite); D_GotoStartingDirectory(ite); Trace(" - Loading MIDI configuration"); // Open MIDI Config file. int fp = -1;//open(MIDIConfigFileName, OREAD); if(fp >= 0) { // TODO: serialise this properly // TODO: actually create a MIDIDataArea //readn(fp, ite->MIDIDataArea, (128+16+9)*32); close(fp); } Trace(" - Initialising playback tables"); Music_Stop(ite); } void Music_ReleasePattern(it_engine *ite, uint16_t ax) { // AX = pattern number // Complete rewrite. if(ite->pat[ax] != NULL) { free(ite->pat[ax]); ite->pat[ax] = NULL; } } it_pattern *Music_GetPattern(it_engine *ite, uint16_t ax) { // This is much simpler. it_pattern *pat = ite->pat[ax]; // SI if(pat == NULL) pat = (it_pattern *)EmptyPattern; return pat; // Here's what happens in the actual code. /* if(AL < 1) { // Empty pattern Push CS Pop DS Mov SI, Offset EmptyPattern Pop AX Ret } else if(AL == 1) { // Start of segment, conventional memory LodsW Mov DS, AX Xor SI, SI Pop AX Ret } else if(AL < 3) { Push CX Push DX MovZX CX, AH Mov DX, [SI] Call E_MapEMSMemory Call E_GetEMSPageFrame Mov DS, AX Xor SI, SI Pop DX Pop CX Pop AX Ret } else { // Start of EMS block LodsW Call E_MapAlignedBlockEMS Pop AX Ret } */ // Basically, a bunch of conventional-vs-EMS crap. } it_pattern *Music_GetPatternLocationNoCount(it_engine *ite, uint16_t ax) { // Because we don't use the x86 realmode segment:offset crap, // this is identical to Music_GetPattern. it_pattern *pat = ite->pat[ax]; if(pat == NULL) pat = (it_pattern *)EmptyPattern; return pat; } it_pattern *Music_GetPatternLocation(it_engine *ite, uint16_t ax, uint16_t *len) { // AX = pattern number // Returns AX = handle // EBX = page/offset or // seg/offset // CX = length it_pattern *pat = Music_GetPattern(ite, ax); *len = pat->Length; return Music_GetPatternLocationNoCount(ite, ax); } it_pattern *Music_AllocatePattern(it_engine *ite, uint16_t dx) { USED(ite); // DX = length. // SI = Pattern // ES:DI points to pattern area // I suspect this is the C version: return malloc(dx); // Yeah. } uint8_t *Music_AllocateSample(it_engine *ite, uint16_t ax, size_t edx) { // AX = Sample number, 0 based // EDX = length // Returns ES:DI, ES = 0 if not. it_sample *smp = &ite->smp[ax]; int real_len = smp->Length; Music_ReleaseSample(ite, ax, 2); edx += 8; // Extra 8 bytes allocated.. if(edx < 1048576) { smp->Flg |= 1; // not in the actual code, just trying to prevent leaks --GM if(ite->SamplePointer[ax] != NULL) { free(ite->SamplePointer[ax]); ite->SamplePointer[ax] = NULL; } // XXX: not sure where this actually goes --GM smp->Length = real_len; ite->SamplePointer[ax] = malloc(edx); return ite->SamplePointer[ax]; } smp->Flg &= 0xF0; return NULL; } void Music_ReleaseSample(it_engine *ite, uint8_t al, uint8_t ah) { // AX = sample number, 0 based // AH = 1 = called from network // = 2 = called from allocate it_sample *smp = &ite->smp[al]; // DS:BX points to sample ite->d.DriverReleaseSample(ite, smp); if((smp->Flg & 1) != 0) { if(ite->SamplePointer[al] != NULL) { free(ite->SamplePointer[al]); ite->SamplePointer[al] = NULL; } } smp->Length = 0; //smp->SamplePointer = 0; // TODO: find out what to do with this --GM if(ah > 1) return; if(ah != 1) { #if NETWORKENABLED Network_AddWordToQueue(ite, (ah<<8) | NETWORK_DELETESAMPLEOBJECT); #endif } smp->Flg &= 0xFE; // this is where we do a good thing and actually use our struct properly // IT itself just does memset(smp->Name, 0, 0x3C). --GM // also, why isn't the filename scrubbed? --GM memset(smp->Name, 0, 26); smp->Cvt = 0; smp->DfP = 0; smp->Length = 0; smp->Loop_Begin = 0; smp->Loop_End = 0; smp->C5Speed = 0; smp->SusLoop_Begin = 0; smp->SusLoop_End = 0; smp->ViS = 0; smp->ViD = 0; smp->ViR = 0; smp->ViT = 0; } void Music_ClearSampleName(it_engine *ite, uint16_t ax) { // AX = Sample number (0 based) memcpy(&ite->smp[ax], SampleHeader, 80); // not in the actual code, just here to prevent a leak --GM if(ite->SamplePointer[ax] != NULL) { free(ite->SamplePointer[ax]); ite->SamplePointer[ax] = NULL; } } void Music_ClearAllSampleNames(it_engine *ite) { int i; for(i = 99; i >= 0; i--) Music_ClearSampleName(ite, i); } void Music_ReleaseAllSamples(it_engine *ite) { int i; for(i = 99; i >= 0; i--) Music_ReleaseSample(ite, i, 0); } void Music_ReleaseAllPatterns(it_engine *ite) { int i; for(i = 99; i >= 0; i--) Music_ReleasePattern(ite, i); } void Music_ClearInstrument(it_engine *ite, uint16_t ax) { // AX = Instrument number // (0 based) memcpy(&ite->ins[ax], InstrumentHeader, 554); } void Music_ClearAllInstruments(it_engine *ite) { int i; for(i = 99; i >= 0; i--) Music_ClearInstrument(ite, i); } void Music_UnInitMusic(it_engine *ite) { Music_UnInitSoundCard(ite); Music_UnloadDriver(ite); Music_ReleaseAllPatterns(ite); Music_ReleaseAllSamples(ite); } // NOT IMPLEMENTING: Music_GetSongSegment // BECAUSE: it's legacy DOS crap that doesn't apply --GM void Music_UnloadDriver(it_engine *ite) { USED(ite); // really not sure how to go about this one. // might just opt for dlclose() or something like that --GM /* Xor AX, AX XChg AX, CS:SoundDriverSegment Test AX, AX JZ Music_UnloadDriver1 Mov ES, AX Mov AH, 49h Int 21h Music_UnloadDriver1: Ret */ } // these have been reworked a bit int NoFunction(it_engine *ite) { USED(ite); return -1; } int NoFunctionB(it_engine *ite, uint8_t w1) { USED(ite, w1); return -1; } int NoFunctionW(it_engine *ite, uint16_t w1) { USED(ite, w1); return -1; } int NoFunctionWW(it_engine *ite, uint16_t w1, uint16_t w2) { USED(ite, w1, w2); return -1; } int NoFunctionPs(it_engine *ite, it_sample *ps1) { USED(ite, ps1); return -1; } const char *NoFunction2(it_engine *ite) { USED(ite); return NoSoundCardMsg; } const char *NoFunction2PccWW(it_engine *ite, const char *c1, uint16_t w2, uint16_t w3) { USED(ite, c1, w2, w3); return NoSoundCardMsg; } void Music_ClearDriverTables(it_engine *ite) { // Makes all of them point to // Xor AX, AX, StC, RetF ite->d.DriverDetectCard = NoFunction2PccWW; ite->d.DriverInitSound = NoFunction2; // we have to serialise this, really, // thus, no OLDDRIVER distinction --GM ite->d.DriverReinitSound = NoFunction; ite->d.DriverUninitSound = NoFunction; ite->d.DriverPoll = NoFunctionWW; ite->d.DriverSetTempo = NoFunctionW; ite->d.DriverSetMixVolume = NoFunctionW; ite->d.DriverSetStereo = NoFunctionW; ite->d.DriverLoadSample = NoFunctionW; ite->d.DriverReleaseSample = NoFunctionPs; ite->d.DriverResetMemory = NoFunction; ite->d.DriverGetStatus = NoFunction; ite->d.DriverSoundCardScreen = NoFunction; ite->d.DriverGetVariable = NoFunctionW; ite->d.DriverSetVariable = NoFunctionWW; ite->d.DriverMIDIOut = NoFunctionB; ite->d.DriverGetWaveform = NoFunction; ite->d.DriverMaxChannels = 32; } int Music_LoadDriver(it_engine *ite, const char *fname) { // pretty much a complete rewrite! let's go! --GM // dlopen() is handled on every modern OS that isn't Windows. // of course, it's easier to just statically link in a driver. // so, no dlopen(). yet. it_drvdata *drv = DriverSys_GetByName(ite, fname); if(drv == NULL) { Music_ClearDriverTables(ite); return -1; } memcpy(&ite->d, drv, sizeof(it_drvdata)); // There's no need to worry about "required variables" // or "required functions" - driver has full it_engine access. return 0; } const char *Music_AutoDetectSoundCard(it_engine *ite) { int dord = 0; const char *reply = NULL; // Returns DS:SI = string // AX, BX, CX, DX, DI = parameters D_GotoStartingDirectory(ite); if(ite->DriverName != NULL) { Trace(" - Loading specific soundcard driver"); if(ite->DriverName[0] != '\x00') { if(!Music_LoadDriver(ite, ite->DriverName)) { reply = ite->d.DriverDetectCard( ite, ite->DriverName, 1, TRACKERVERSION); // Forced // Unfortunately these drivers don't have to return 'Jeff'. if(reply != NULL) { Music_UnloadDriver(ite); Music_ClearDriverTables(ite); } } } } else for(;;) { //Music_AutoDetectSoundCard1: Trace(" - Testing soundcard driver"); int didx = DriverDetectionOrder[dord++]; if(didx == 0xFFFF) break; if(!Music_LoadDriver(ite, DriverNameTable[didx])) { const char *reply = ite->d.DriverDetectCard( ite, DriverNameTable[didx], 1, TRACKERVERSION); // Not forced if(reply == NULL) break; Music_UnloadDriver(ite); Music_ClearDriverTables(ite); } } GetChannels(ite); ite->d.DriverInitSound(ite); // Not sure what these are for! /* Mov [CS:ADSCParams+2], DS Mov [ADSCParams], SI Mov [ADSCParams+12], DI Mov [ADSCParams+10], DX Mov [ADSCParams+8], CX Mov [ADSCParams+6], BX Mov [ADSCParams+4], AX */ Music_InitStereo(ite); Music_InitMixTable(ite); return reply; } void Music_ShowAutoDetectSoundCard(it_engine *ite) { USED(ite); // HELP --GM /* Push DWord Ptr [ADSCParams+10] Push DWord Ptr [ADSCParams+6] Push [ADSCParams+4] LDS SI, DWord Ptr [ADSCParams] Mov AH, 20h Mov DI, (26+28*80)*2 Call S_DrawString Add SP, 10 Ret */ } uint16_t Music_GetInstrumentMode(it_engine *ite) { return (ite->hdr.Flags & 4); } void UpdateGOTONote(it_engine *ite) { // Get offset & arrayed flag. uint16_t patnum; uint16_t maxrow; it_pattern *pat = PE_GetCurrentPattern(ite, &patnum, &maxrow); USED(pat); // AX = Pattern number // BX = MaxRow // DS = PatternDataSegment if(patnum == ite->CurrentPattern) { ite->DecodeExpectedPattern = patnum; //Mov CS:PatternSegment, DS ite->NumberOfRows = maxrow; if(ite->ProcessRow >= ite->NumberOfRows) { ite->ProcessRow = 0; ite->CurrentRow = 0; } ite->DecodeExpectedRow = ite->ProcessRow; ite->PatternOffset = ite->ProcessRow * (64*5); ite->PatternArray = 1; return; } ite->DecodeExpectedPattern = ite->CurrentPattern; #if NETWORKENABLED Network_UpdatePatternIfIdle(ite); #endif pat = Music_GetPattern(ite, ite->CurrentPattern); // DS:SI points to pattern. // AX = number of rows if(ite->ProcessRow >= pat->Rows) ite->CurrentRow = 0; else ite->CurrentRow = ite->ProcessRow; ite->ProcessRow = ite->CurrentRow; ite->DecodeExpectedRow = ite->CurrentRow; ite->NumberOfRows = pat->Rows; uint8_t *data = pat->data; uint16_t cx = ite->ProcessRow; uint8_t al; cx++; while((--cx) != 0) { for(;;) { // OK.. now to find the right // offset & update tables. al = *(data++); if(al == 0) break; uint8_t dl = al; al = (al & 0x7F) - 1; it_host *chn = &ite->chn[al]; // CS:DI points. if((dl & 0x80) != 0) chn->Msk = *(data++); if((chn->Msk & 1) != 0) chn->Nte = *(data++); if((chn->Msk & 2) != 0) chn->Ins = *(data++); if((chn->Msk & 4) != 0) chn->Vol = *(data++); if((chn->Msk & 8) != 0) { // warning, when reading the code this can catch you out. // it loads then stores a *word*. // the IT code does this kind of stuff a lot. --GM chn->Cmd = *(data++); chn->CVal = *(data++); } } } ite->PatternOffset = data - pat->data; ite->PatternArray = 0; } void PreInitCommand(it_engine *ite, it_host *chn) { if((chn->Msk & 0x33) != 0) { if((ite->hdr.Flags & 4) == 0 || chn->Nte >= 120 || chn->Ins == 0) { chn->Nt2 = chn->Nte; chn->Smp = chn->Ins; } else { // Have to xlat. it_instrument *ins = &ite->ins[chn->Ins-1]; // XXX: work around a modplugism if(ins->MCh == 0 || ins->MCh >= 128) { if(ins->MCh >= 128) printf("MODPLUGISM WARNING: midi channel %02X used for effect slot - WILL PROBABLY CRASH IT\n", ins->MCh); chn->Nt2 = ins->NoteSamp[chn->Nte][0]; chn->Smp = ins->NoteSamp[chn->Nte][1]; // This part is fine. //printf("XLAT %i -> %i %i\n", chn->Nte, chn->Nt2, chn->Smp); } else { if(ins->MCh == 17) chn->MCh = chn->HCN+1; else chn->MCh = ins->MCh; chn->MPr = ins->MPr; chn->Nt2 = ins->NoteSamp[chn->Nte][0]; chn->Smp = 101; } if(chn->Smp == 0) // No sample? return; } } InitCommandTable[chn->Cmd & 0x1F](ite, chn); // Init note chn->Flags |= 64; // Check whether chn is on if((chn->HCN & 0x80) == 0) return; if((chn->Flags & 32) != 0) return; if((chn->Flags & 4) == 0) return; // Channel was off. it_slave *slave = &ite->slave[chn->SCOffst]; slave->Flags |= 0x0800; } void UpdateNoteData(it_engine *ite) { ite->PatternLooping = 0; if(ite->CurrentPattern == ite->DecodeExpectedPattern) { UpdateGOTONote(ite); } else { ite->DecodeExpectedRow++; if(ite->CurrentRow != ite->DecodeExpectedRow) UpdateGOTONote(ite); } uint16_t cx = 64; // 64 channels // Xor AX, AX // Just to get rid of "jumps" it_host *chn = &ite->chn[0]; // DI --GM if(ite->PatternArray != 1) { // First clear all old command&value. // Mov CX, 64 // Done above for(; cx != 0; cx--) { chn->Flags &= ~(3+32+64+256); chn++; } //Mov AX, CurrentPattern #if NETWORKENABLED // Network_UpdatePatternIfIdle(ite); #endif it_pattern *pat = Music_GetPattern(ite, ite->CurrentPattern); // Gets DS uint8_t *data = pat->data + ite->PatternOffset; for(;;) { uint8_t al; al = *(data++); if(al == 0) break; // No more! // else... go through decoding. uint8_t dl = al; assert((al & 0x7F) != 0); al = (al & 0x7F) - 1; chn = &ite->chn[al]; uint8_t dh = chn->Msk; // mask. if((dl & 0x80) != 0) { dh = *(data++); chn->Msk = dh; } if((dh & 1) != 0) chn->Nte = *(data++); if((dh & 2) != 0) chn->Ins = *(data++); if((dh & 4) != 0) chn->Vol = *(data++); uint8_t ah; if((dh & 8) != 0) { al = chn->OCm = *(data++); ah = chn->OVal = *(data++); } else if((dh & 0x80) != 0) { al = chn->OCm; ah = chn->OVal; } else { al = ah = 0; } chn->Cmd = al; chn->CVal = ah; PreInitCommand(ite, chn); } ite->PatternOffset = data - pat->data; } else { uint8_t *data = (&ite->patspace[0]) + ite->PatternOffset; // Mov CX, 64 ; 64 channels // Mov DI, Offset HostChannelInformationTable for(; cx != 64; cx--) { uint8_t dl = 0; // DL = mask. chn->Flags &= ~(3+32+64+256); // Turn off all calling... uint8_t al; al = *(data++); if(al != NONOTE) { chn->Nte = al; dl |= 1; } al = *(data++); if(al != 0) { chn->Ins = al; dl |= 2; } al = *(data++); if(al != 0xFF) { chn->Vol = al; dl |= 4; } al = *(data++); uint8_t ah = *(data++); chn->Cmd = chn->OCm = al; chn->CVal = chn->OVal = ah; if(al != 0 || ah != 0) dl |= 8; if(dl != 0) { chn->Msk = dl; PreInitCommand(ite, chn); } chn++; } ite->PatternOffset = data - ite->patspace; } } void UpdateVibrato(it_engine *ite, it_slave *slave) { // DS:SI points to slavechannelstruct. it_sample *smp = &ite->smp[slave->SmpOffs]; // ES:BX points to sample if(smp->ViD == 0) return; // ITTECH.TXT lied to you - check the original source. // it misses a crucial step... and IT uses CX not AX --GM slave->ViDepth += smp->ViR; if((slave->ViDepth>>8) > smp->ViD) slave->ViDepth = (slave->ViDepth & 0xFF) | (((uint16_t)smp->ViD)<<8); if(smp->ViS == 0) return; int8_t al = 0; if(smp->ViT != 3) { slave->ViP += smp->ViS; // Update pointer. // probably not wise to try to emulate out-of-range vibrato types. // well, at least for now. we can research these later. --GM switch(smp->ViT) { case 0: al = FineSineData[slave->ViP]; break; case 1: al = FineRampDownData[slave->ViP]; break; case 2: al = FineSquareWave[slave->ViP]; break; default: printf("PANIC: out of range vibrato types not emulated!\n"); abort(); } } else { al = ((Random(ite) & 0x7F) - 64); } int16_t ax = ((int16_t)al) * (int16_t)(slave->ViDepth>>8); ax >>= 6; // SAL 2, take high (SAR 8) .: SAR 6 #if !USEFPUCODE if(ax < 0) { // strictly speaking this branch doesn't look for the host channel, // but it's included for completeness --GM PitchSlideDownLinear(ite, &ite->chn[slave->HCOffst], slave, -ax); } else { #endif PitchSlideUpLinear(ite, &ite->chn[slave->HCOffst], slave, ax); #if !USEFPUCODE } #endif } void Update(it_engine *ite, uint16_t *rcx, it_slave **si, uint16_t *ax) { uint16_t cx = MAXSLAVECHANNELS; it_slave *slave = &ite->slave[0]; it_host *chn = &ite->chn[slave->HCOffst]; MIDITranslate(ite, chn, slave, MIDICOMMAND_TICK); for(; cx != 0; cx--) { if((slave->Flags & 1) != 0) { // reset volume if(slave->VS != slave->Vol) { slave->Vol = slave->VS; slave->Flags |= 0x10; } // Freq if(slave->Frequency_Set != slave->Frequency) { slave->Frequency = slave->Frequency_Set; slave->Flags |= 0x20; // Recalc freq. } } slave++; } UpdateData(ite); if((ite->hdr.Flags & 4) != 0) { UpdateInstruments(ite); } else { UpdateSamples(ite); } *rcx = ite->NumChannels; *si = &ite->slave[0]; *ax = ite->PlayMode; } void UpdateSamples(it_engine *ite) { uint16_t i = ite->NumChannels; it_slave *slave = &ite->slave[0]; for(; i != 0 ; i--, slave++) { if((slave->Flags & 1) == 0) continue; // OK... if recalc volume is on. // then recalc volume! :) if((slave->Flags & 16) != 0) { slave->Flags &= ~16; slave->Flags |= 64; // Recalc final vol if(ite->SoloSample != 0xFF && ite->SoloSample != slave->Smp) slave->Flags |= 0x0800; uint32_t ax = ((uint32_t)slave->Vol)*((uint32_t)slave->CVl) *((uint32_t)slave->SVl); // AX = 0->64*64*128 ax >>= 4; // AX = 0->32768 ax *= (uint32_t)ite->GlobalVolume; // AX = 0->32768*128 ax >>= 7; // Final vol stored. slave->FV = ax>>8; slave->_16bVol = ax; } if((slave->Flags & 2) != 0) { slave->Flags &= ~2; slave->Flags |= 0x8000; slave->FP = slave->Pan; if(slave->Pan != 100) { int16_t ax = slave->Pan; ax -= 32; // Pan = (Pan-32)* Separation/128 + 32 ax *= (int16_t)ite->hdr.Sep; // 0->64 (Separation) => AX = -2048->+2048 // ie. AH = -8->+8 ax >>= 7; // AL = -32->+32 if(ite->d.ReverseChannels != 0) ax = -ax; ax += 32; slave->FPP = ax; } else { slave->FPP = slave->Pan; } } UpdateVibrato(ite, slave); } } int UpdateEnvelope(it_engine *ite, it_slen *slen, it_envelope *env, uint16_t bp) { USED(ite); // Returns Carry if envelope needs // to be turned off // Reqs ES:DI points to envelope // DS:SI points to slave channel envelope structure // Called only if envelope is ON // BP != 0 if sustain points released, 0 otherwise int32_t dx = slen->EnvPos; // DX = current pos if(dx < slen->NextET) { // Increase position; slen->EnvPos = ++dx; // Update value slen->EnvelopeValue += slen->EnvelopeDelta; return 0; } // Procedure: // 1) Get current pos' value // 2) Figure out next pos (inc/loop) // 3) Figure out delta to next pos // 4) Terminate if no loop (with carry) // or place new check in [SI+0Ch] uint16_t bx = slen->CurEnN; // BX = cur env node; uint16_t ax = bx+1; dx = (int8_t)env->Nodes[bx][0]; dx <<= 16; slen->EnvelopeValue = dx; // Current pos value done. // AX = next cur env node if((env->Flg & 6) != 0) // Any loop at all? { // Normal Loop uint8_t bl = env->LpB; uint8_t bh = env->LpE; // the flow here is very weird in the original source --GM int use_sus = ((env->Flg & 4) != 0) && (bp == 0); int has_loop = (use_sus ? (env->Flg & 4) : (env->Flg & 2)) != 0; if(use_sus) { bl = env->SLB; bh = env->SLE; } // Loop if(has_loop && ax > bh) { // BL = New node slen->CurEnN = bl; uint16_t x = ((uint16_t)env->Nodes[bl][1]) | (((uint16_t)env->Nodes[bl][2])<<8); slen->EnvPos = x; slen->NextET = x; return 0; } } // AX = new node if(ax >= env->Num) return 1; //slen->CurEnN = ax; // New node slen->CurEnN = (slen->CurEnN & 0xFF00) | (ax & 0x00FF); // New node // New node's tick uint16_t x1 = ((uint16_t)env->Nodes[ax][1]) | (((uint16_t)env->Nodes[ax][2])<<8); slen->NextET = x1; uint16_t x0 = ((uint16_t)env->Nodes[ax-1][1]) | (((uint16_t)env->Nodes[ax-1][2])<<8); uint16_t delta = x1 - x0; uint16_t base = x0+1; slen->EnvPos = base; // For security. int32_t yd = (int32_t)(((int8_t)env->Nodes[ax][0]) - ((int8_t)env->Nodes[ax-1][0])); int isneg = (yd < 0); if(isneg) yd = -yd; // Just to prevent div 0 errors if(delta == 0) delta = 1; // this was hell to translate --GM uint32_t eax = ((uint32_t)yd)/(uint32_t)delta; uint32_t edx = ((uint32_t)yd)%(uint32_t)delta; yd = (eax<<16) | ((edx<<16)/(uint32_t)delta); if(isneg) yd = -yd; slen->EnvelopeDelta = yd; // Delta done return 0; } void UpdateMIDI(it_engine *ite) { uint16_t cx; it_slave *slave; it_host *chn; // Stop cycle for(cx = MAXSLAVECHANNELS, slave = &ite->slave[0]; cx != 0; cx--, slave++) { if((slave->Flags & 0x200) == 0) continue; if(slave->Smp != 100) continue; if((slave->Flags & 1) == 0) continue; slave->Flags = 0; chn = &ite->chn[slave->HCOffst]; MIDITranslate(ite, chn, slave, MIDICOMMAND_STOPNOTE); if((slave->HCN & 0x80) != 0) continue; slave->HCN |= 0x80; chn->Flags &= ~4; // Signify channel off } // Play cycle for(cx = MAXSLAVECHANNELS, slave = &ite->slave[0]; cx != 0; cx--, slave++) { if(slave->Smp != 100) continue; // MIDI Instrument? if((slave->Flags & 1) == 0) continue; slave->OldSampleOffset = (slave->OldSampleOffset & ~0xFF) | (slave->Sample_Offset & 0xFF); slave->Sample_Offset = (slave->Sample_Offset & ~0xFF) | 1; if((slave->Smp & 0x0800) != 0) continue; // Muted? chn = &ite->chn[slave->HCOffst]; slave->Flags &= 0x788D; // 0111100010001101b // 0111 1000 1000 1101 // 7 8 8 D if((slave->Flags & 0x0100) != 0) { // Check if there's a bank select. uint16_t MBank = ((uint16_t)slave->FCut) | (((uint16_t)slave->FRes)<<8); if(MBank != 0xFFFF) { if(ite->MIDIBanks[slave->MCh-1] != MBank) { ite->MIDIBanks[slave->MCh-1] = MBank; ite->MIDIPrograms[slave->MCh-1] = 0xFF; // Reset program MIDITranslate(ite, chn, slave, MIDICOMMAND_BANKSELECT); } } // Check for a program specification if(((int8_t)slave->MPr) >= 0) { if(ite->MIDIPrograms[slave->MCh-1] != slave->MPr) { ite->MIDIPrograms[slave->MCh-1] = slave->MPr; MIDITranslate(ite, chn, slave, MIDICOMMAND_PROGRAMSELECT); } } // Check for MIDI pitch wheel.. if((ite->hdr.Flags & 64) != 0) { if(ite->MIDIPitch[slave->MCh-1] != 0x2000) { ite->MIDIPitch[slave->MCh-1] = 0x2000; MIDISendFilter(ite, chn, (slave->MCh-1) | 0xE0); MIDISendFilter(ite, chn, 0); // Reset pitch wheel MIDISendFilter(ite, chn, 0x40); } } slave->RVol_MIDIFSet = slave->Frequency_Set; MIDITranslate(ite, chn, slave, MIDICOMMAND_PLAYNOTE); } else { // Change in volume? if((slave->Flags & 64) != 0) MIDITranslate(ite, chn, slave, MIDICOMMAND_CHANGEVOLUME); } // Pan changed? if((slave->Flags & 0x8000) != 0) { if(ite->MIDIPan[slave->MCh-1] != slave->FPP) { slave->FPP = ite->MIDIPan[slave->MCh-1]; MIDITranslate(ite, chn, slave, MIDICOMMAND_CHANGEPAN); } } // Pitch changed? if((slave->Flags & 32) != 0) MIDITranslate(ite, chn, slave, MIDICOMMAND_CHANGEPITCH); } } void UpdateInstruments(it_engine *ite) { // Things to update: // 1) Volume envelope // 2) Fadeout // 3) FinalVolume // 4) Vibrato. // Turn off channel if // 1) Volume envelope is off & VEV = 0 or // 2) Fadeout = 0 uint16_t cx; it_slave *slave; ite->DoMIDICycle = 0; for(cx = MAXSLAVECHANNELS, slave = &ite->slave[0]; cx != 0; cx--, slave++) { if((slave->Flags & 1) != 0) UpdateInstruments16(ite, slave); // Channel on!! } if(ite->DoMIDICycle != 0) UpdateMIDI(ite); } void UpdateInstruments16(it_engine *ite, it_slave *slave) { // Mov CX, [SI] it_instrument *ins = &ite->ins[slave->InsOffs-1]; if(slave->Ins == 0xFF) { // No instrument? UpdateInstruments5(ite, slave); return; } uint16_t bp = (slave->Flags & 4); // BP = sustain for envelope calls //printf("InsOffs %i %i %i %04X\n", slave->InsOffs, slave->Ins, bp, slave->Flags); if((slave->Flags & 0x4000) != 0) if(UpdateEnvelope(ite, &slave->Pt, &ins->PitchEnv, bp) != 0) slave->Flags &= ~0x4000; int do_filter = ((ins->PitchEnv.Flg & 0x80) != 0); if(do_filter && slave->Smp != 100) { int16_t fval = (slave->Pt.EnvelopeValue>>8); fval >>= 6; // Range -128 to +128 fval += 128; // Range 0 -> 256 if(fval >= 0x100) fval--; slave->FCut = fval; slave->Flags |= 64; // Recalculate final volume } else if(!do_filter) { int16_t ptval = (slave->Pt.EnvelopeValue>>8); ptval >>= 3; if(ptval != 0) { #if !USEFPUCODE if(ptval >= 0) { #endif PitchSlideUpLinear(ite, &ite->chn[slave->HCOffst], slave, ptval); #if !USEFPUCODE } else { PitchSlideDownLinear(ite, &ite->chn[slave->HCOffst], slave, -ptval); } #endif slave->Flags |= 32; // Recalculate freq } } if((slave->Flags & 0x2000) != 0) { slave->Flags |= 2; // Recalculate pan if(UpdateEnvelope(ite, &slave->P, &ins->PanEnv, bp) != 0) slave->Flags &= ~0x2000; } // Volume envelope on? int jump_ins17 = 0; if((slave->Flags & 0x1000) != 0) { slave->Flags |= 16; // Recalculate volume if(UpdateEnvelope(ite, &slave->V, &ins->VolEnv, bp) == 0) { // Note fade on? if((slave->Flags & 8) != 0) { // Now, check if loop + sustain // off // TODO: verify - i may have missed a bp modification --GM if(bp == 0) { UpdateInstruments5(ite, slave); return; } // Normal vol env loop? if((ins->VolEnv.Flg & 2) == 0) { UpdateInstruments5(ite, slave); // Volume calculation return; } } } else { // Envelope turned off... slave->Flags &= ~0x1000; // Turn off envelope flag // Turn off if end of loop is reached // TODO: verify - this looks weird if(((slave->V.EnvelopeValue>>16)&0xFF) == 0) jump_ins17 = 1; } } else { if((slave->Flags & 8) == 0) // Note fade?? { // Also apply fade if No vol env // AND sustain off // Note off issued? if((slave->Flags & 4) == 0) { UpdateInstruments5(ite, slave); return; } } } if(!jump_ins17) { // in the original, this line is skipped when the bit is set // maintaining that would just make for a horrible flow --GM slave->Flags |= 8; slave->FadeOut -= ins->FadeOut; if(slave->FadeOut <= 0) { slave->FadeOut = 0; } else { slave->Flags |= 16; // Recalc volume flag. UpdateInstruments5(ite, slave); return; } } // Turn off channel if((slave->HCN & 0x80) == 0) { slave->HCN |= 0x80; // Host channel exists ite->chn[slave->HCOffst].Flags &= ~4; } slave->Flags |= 0x0200; slave->Flags |= 16; // Recalc volume flag. UpdateInstruments5(ite, slave); } void UpdateInstruments5(it_engine *ite, it_slave *slave) { if((slave->Flags & 16) != 0) { // Calculate volume slave->Flags &= ~16; //Mov DX, Word Ptr [SoloSample] ; DL = sample, DH = inst slave->Flags |= 64; // Recalc final volume uint32_t eax = slave->Vol; // Note volume... eax *= (uint32_t)slave->CVl; // Channel volume // AX = (0->4096) if(ite->SoloSample != 0xFF && ite->SoloSample != slave->Smp) slave->Flags |= 0x0800; else if(ite->SoloInstrument != 0xFF && ite->SoloInstrument != slave->Ins) slave->Flags |= 0x0800; eax *= slave->FadeOut; // Fadeout Vol (0->1024) // DX:AX = 0->4194304 eax >>= 7; // AX = (0->32768) eax *= slave->SVl; // Sample volume // DX:AX = 0->4194304 eax >>= 7; // AX = 0->32768 eax *= (0xFFFF & (slave->V.EnvelopeValue>>8)); // VEV, 0->64*256 // DX:AX = 0->536870912 eax >>= 14; // AX = 0->32768 eax *= ite->GlobalVolume; // DX:AX = 0->4194304 eax >>= 7; //printf("VOL %04X %02X\n", slave->_16bVol, slave->FV); slave->FV = eax>>8; slave->_16bVol = eax; } // Change in panning? if((slave->Flags & 2) != 0) { slave->Flags &= ~2; slave->Flags |= 0x8000; int16_t ax = slave->Pan; // actual pan if(ax != 100) { // some really funky stuff going on here. // i could possibly simplify this but i'd like to be accurate. // so, here we go for now --GM ax = 32 - ax; ax ^= (ax>>8); ax = (ax & 0xFF00) | ((ax - (ax>>8)) & 0xFF); // AL = |32-ActualPan| ax = (ax & 0xFF00) | ((32 - ax) & 0xFF); ax = (ax & 0xFF) * (int16_t)(int8_t)( slave->Pt.EnvelopeValue>>16); // Pan envelope.. ax >>= 5; ax = (ax & 0xFF00) + ((ax + slave->Pan) & 0xFF); // Value to show.. slave->Pan = ax; ax = (ax & 0xFF00) + ((ax - 32) & 0xFF); // Pan = (Pan-32)* Separation/128 + 32 uint8_t sep = ite->hdr.Sep; // 0->64 (Separation) sep >>= 1; ax = (ax & 0xFF) & (int8_t)sep; // AX = -2048->+2048 // ie. AH = -8->+8 ax >>= 6; // AL = -32->+32 if(ite->d.ReverseChannels != 0) ax = -ax; ax += 32; } else { slave->FP = ax; } slave->FPP = ax; } UpdateVibrato(ite, slave); if(slave->Smp == 100) // MIDI? ite->DoMIDICycle = 1; } void UpdateData(it_engine *ite) { if(ite->PlayMode == 1) UpdateData_PlayMode1(ite); else if(ite->PlayMode < 1) UpdateData_PlayMode0(ite); else UpdateData_PlayMode2(ite); } void UpdateData_PlayMode0(it_engine *ite) { uint16_t cx; it_host *chn; // Step through each channel. // Check if on/updatemode OK for(cx = 64, chn = &ite->chn[0]; cx != 0; cx--, chn++) { if(chn->CUC == 0) continue; chn->CUC--; // Handle counter if(chn->CUC != 0xFF) chn->Flags &= ~0x303; // Turn off mode. // Mov AX, [DI] // Channel on? / Don't update effect? if((chn->Flags & 4) != 0 && (chn->Flags & 0x100) != 0) VolumeEffectTable[chn->VCm & 7](ite, chn); if((chn->Flags & 2) != 0 // Update effect regardless.. || ((chn->Flags & 4) != 0 && (chn->Flags & 1) != 0)) { // OK. now handle counter. CommandTable[chn->Cmd & 31](ite, chn); } // Progress to next channel } // That's all! } void UpdateData_NoNewRow(it_engine *ite) { it_host *chn; uint16_t cx; // OK. call update command. for(chn = &ite->chn[0], cx = 64; cx != 0; cx--, chn++) { if((chn->Flags & 4) != 0 && (chn->Flags & 0x100) != 0) VolumeEffectTable[chn->VCm & 7](ite, chn); if((chn->Flags & 3) == 0) continue; if((chn->Flags & 2) == 0 && (chn->Flags & 4) == 0) continue; //printf("%i %04X\n", chn->Flags); CommandTable[chn->Cmd & 31](ite, chn); } } void UpdateData_PlayMode1(it_engine *ite) { // Pattern stuff.. ite->ProcessTick--; ite->CurrentTick--; if(ite->CurrentTick != 0) { UpdateData_NoNewRow(ite); return; } // OK... have to update row. ite->CurrentTick = ite->ProcessTick = ite->CurrentSpeed; ite->RowDelay--; if(ite->RowDelay != 0) { UpdateEffectData(ite); return; } ite->RowDelay = 1; uint16_t ax = ite->ProcessRow+1; // Progress to new row. if(ax >= ite->NumberOfRows) { if(ite->d.StopEndOfPlaySection != 0) { Music_Stop(ite); // Optionally.. loop! return; } // Wrap row. ax = ite->BreakRow; ite->BreakRow = 0; } ite->ProcessRow = ite->CurrentRow = ax; // Gotta get note data. UpdateNoteData(ite); return; } void UpdateEffectData(it_engine *ite) { uint16_t cx; it_host *chn; for(chn = &ite->chn[0], cx = 64; cx != 0; cx--, chn++) { if((chn->Flags & 64) == 0) continue; if((chn->Msk & 0x88) == 0) continue; uint8_t oldmsk = chn->Msk; chn->Msk &= 0x88; InitCommandTable[chn->Cmd & 0x1F](ite, chn); // Init note chn->Msk = oldmsk; } } void UpdateData_PlayMode2(it_engine *ite) { ite->ProcessTick--; ite->CurrentTick--; if(ite->CurrentTick != 0) { UpdateData_NoNewRow(ite); return; } // Play song stuff... ite->CurrentTick = ite->ProcessTick = ite->CurrentSpeed; ite->RowDelay--; if(ite->RowDelay != 0) { UpdateEffectData(ite); return; } ite->RowDelay = 1; uint16_t ax = ite->ProcessRow+1; if(ax < ite->NumberOfRows) { ite->ProcessRow = ite->CurrentRow = ax; UpdateNoteData(ite); return; } if((ite->OrderLockFlag & 1) == 0) { int dx = 0; // Get new pattern. int16_t bx = ite->ProcessOrder + 1; uint8_t cl; // might as well emulate any side-effects we get --GM for(;;) { if(bx < 0x100) { // next pattern cl = ite->ord[bx]; // CL = next pattern. if(cl < 200) break; bx++; if(cl == 0xFE) continue; ite->StopSong = 1; if(ite->d.StopEndOfPlaySection != 0) { Music_Stop(ite); // Optionally.. loop! return; } } if(dx != 0) { Music_Stop(ite); // Optionally.. loop! return; } dx++; bx = 0; } ite->ProcessOrder = ite->CurrentOrder = bx; ite->CurrentPattern = cl; } ite->ProcessRow = ite->CurrentRow = ite->BreakRow; ite->BreakRow = 0; UpdateNoteData(ite); } uint16_t Music_GetNumberOfSamples(it_engine *ite) { // Returns AX uint16_t ax = 99; it_sample *smp = &ite->smp[99-1]; while(ax > 0) { if(memcmp(smp, SampleHeader, 80)) break; ax--; smp--; } return ax; } uint16_t Music_GetNumberOfInstruments(it_engine *ite) { // Returns AX uint16_t ax = 99; it_instrument *ins = &ite->ins[99-1]; while(ax > 0) { if(memcmp(ins, InstrumentHeader, 80)) break; ax--; ins--; } return ax; } it_sample *Music_GetSampleHeader(it_engine *ite, uint16_t ax) { // AX = sample, 1 based return &ite->smp[ax-1]; } uint8_t *Music_GetSampleLocation(it_engine *ite, uint16_t ax, uint32_t *rcx, int *is8bit) { // AX = sample (1based) // CH = page. // Returns DS:ESI // ECX = length // Carry if no sample. - (using NULL --GM) // Zero set if 8 bit (using a flag --GM) ite->LastSample = ax; it_sample *smp = &ite->smp[ax-1]; if((smp->Flg & 1) == 0) return NULL; if(ite->SamplePointer[ax-1] == NULL) return NULL; uint8_t *data = ite->SamplePointer[ax-1]; *rcx = smp->Length; *is8bit = ((~smp->Flg) & 1); return data; } // Accessed via Int 3 uint8_t *Music_UpdateSampleLocation(it_engine *ite, uint32_t esi, int *is8bit) { USED(esi); // Reqs ESI. // TODO: decipher this crap *properly* --GM uint16_t ax = ite->LastSample; uint32_t dummy_ecx; return Music_GetSampleLocation(ite, ax, &dummy_ecx, is8bit); } uint8_t *Music_FarUpdateSampleLocation(it_engine *ite, uint32_t esi, int *is8bit) { return Music_UpdateSampleLocation(ite, esi, is8bit); } void Music_GetPlayMode(it_engine *ite, uint16_t *PlayMode, uint16_t *CurrentRow, uint16_t *CurrentPattern, uint16_t *CurrentOrder, uint16_t *NumberOfRows) { *PlayMode = ite->PlayMode; *CurrentRow = ite->CurrentRow; *CurrentPattern = ite->CurrentPattern; *CurrentOrder = ite->CurrentOrder; *NumberOfRows = ite->NumberOfRows; } void Music_GetPlayMode2(it_engine *ite, uint16_t *PlayMode, uint16_t *CurrentRow, uint16_t *CurrentTick, uint32_t *CurrentOrder) { *PlayMode = ite->PlayMode; *CurrentOrder = ite->CurrentOrder; *CurrentRow = ite->CurrentRow; *CurrentTick = ite->CurrentTick; } void Music_PlayPattern(it_engine *ite, uint16_t pidx, uint16_t numrows, uint16_t startrow) { // AX = pattern, BX = number of rows // CX = row to start Music_Stop(ite); ite->MIDIPitchDepthSent = 0; ite->LastMIDIByte = 0xFF; ite->CurrentPattern = pidx; ite->CurrentRow = startrow; ite->NumberOfRows = numrows; ite->ProcessRow = startrow-1; ite->PlayMode = 1; } void Music_PlaySong(it_engine *ite, uint16_t oidx) { // AX = Order Music_Stop(ite); ite->MIDIPitchDepthSent = 0; ite->LastMIDIByte = 0xFF; ite->CurrentOrder = oidx; ite->ProcessOrder = oidx-1; ite->ProcessRow = 0xFFFE; ite->PlayMode = 2; StartClock(ite); MIDITranslate(ite, ite->chn, ite->slave, MIDICOMMAND_START); } void Music_PlayPartSong(it_engine *ite, uint16_t oidx, uint16_t row) { // AX = order, BX = row. Music_Stop(ite); ite->NumberOfRows = 200; ite->ProcessOrder = oidx; ite->CurrentOrder = oidx; ite->CurrentRow = row; ite->ProcessRow = row-1; ite->CurrentPattern = ite->ord[oidx]; ite->PlayMode = 2; StartClock(ite); } void Music_KBPlaySong(it_engine *ite) { if(ite->PlayMode != 2) Music_PlaySong(ite, 0); } void Music_StopChannels(it_engine *ite) { uint16_t cx; it_host *chn; it_slave *slave; for(chn = &ite->chn[0], cx = 64; cx != 0; cx--, chn++) { chn->Flags = 0; chn->PLR = 0; chn->PLC = 0; } for(slave = &ite->slave[0], cx = MAXSLAVECHANNELS; cx != 0; cx--, slave++) { if((slave->Flags & 1) != 0 && slave->Smp == 100) MIDITranslate(ite, &ite->chn[slave->HCOffst], slave, MIDICOMMAND_STOPNOTE); slave->Flags = 0x200; } } void Music_Stop(it_engine *ite) { uint16_t cx; it_host *chn; it_slave *slave; // Turn off MIDI channels first. if((ite->OrderLockFlag & 1) != 0) Music_ToggleOrderUpdate(ite); for(slave = &ite->slave[0], cx = MAXSLAVECHANNELS; cx != 0; cx--, slave++) { if((slave->Flags & 1) != 0 && slave->Smp == 100) MIDITranslate(ite, &ite->chn[slave->HCOffst], slave, MIDICOMMAND_STOPNOTE); } // Stop MIDITranslate(ite, &ite->chn[0], &ite->slave[0], MIDICOMMAND_STOP); ite->PlayMode = 0; ite->DecodeExpectedPattern = 0xFFFE; ite->DecodeExpectedRow = 0xFFFE; ite->RowDelay = 1; ite->CurrentRow = 0; ite->CurrentOrder = 0; ite->CurrentTick = 1; ite->BreakRow = 0; memset(ite->MIDIPrograms, 0xFF, 32*2); uint8_t dl; uint8_t dh; for(chn = &ite->chn[0], dl = 64, dh = 0; dl != 0; dl--, chn++, dh++) { chn->Flags = 0; chn->Msk = 0; chn->Nte = 0; chn->Ins = 0; chn->Vol = 0; chn->Cmd = 0; chn->CVal = 0; chn->OCm = 0; chn->OVal = 0; chn->VCm = 0; chn->VVal = 0; chn->MCh = 0; chn->MPr = 0; chn->Nt2 = 0; chn->Smp = 0; chn->DKL = 0; chn->EFG = 0; chn->O00 = 0; chn->I00 = 0; chn->J00 = 0; chn->M00 = 0; chn->N00 = 0; chn->P00 = 0; chn->Q00 = 0; chn->T00 = 0; chn->S00 = 0; chn->OxH = 0; chn->W00 = 0; chn->VCE = 0; chn->GOE = 0; chn->SFx = 0; chn->HCN = dh; chn->CUC = 0; chn->VSe = 0; chn->LTr = 0; chn->SCOffst = 0; chn->PLR = 0; chn->PLC = 0; chn->PWF = 0; chn->PPo = 0; chn->PDp = 0; chn->PSp = 0; chn->LPn = 0; chn->LVi = 0; chn->CP = ite->hdr.Chnl_Pan[dh] & 0x7F; chn->CV = ite->hdr.Chnl_Vol[dh]; chn->VCh = 0; chn->TCD = 0; chn->Too = 0; chn->RTC = 0; chn->Porta_Frequency = 0; chn->VWF = 0; chn->VPo = 0; chn->VDp = 0; chn->VSp = 0; chn->TWF = 0; chn->TPo = 0; chn->TDp = 0; chn->TSp = 0; chn->_40 = 0; chn->_42 = 0; chn->_44 = 0; chn->_46 = 0; chn->_47 = 0; chn->_48 = 0; chn->_49 = 0; chn->_4A = 0; chn->_4B = 0; chn->_4C = 0; chn->_4D = 0; chn->_4E = 0; chn->_4F = 0; } // Now clear SlaveChannel //Mov DX, MAXSLAVECHANNELS for(slave = &ite->slave[0], cx = MAXSLAVECHANNELS; cx != 0; cx--, slave++) { // too much bloody effort, using memset --GM //memset(slave, 0, 128); memset(slave, 0, sizeof(it_slave)); slave->Flags = 0x0200; } ite->GlobalVolume = ite->hdr.GV; ite->CurrentSpeed = ite->hdr.IS; ite->ProcessTick = ite->hdr.IS; ite->Tempo = ite->hdr.IT; Music_InitTempo(ite); MIDI_ClearTable(ite); } void Music_UpdatePatternOffset(it_engine *ite) { ite->DecodeExpectedPattern = 0xFFFE; } void Music_PlayNote(it_engine *ite, uint8_t *data, uint16_t cidx, uint8_t dh) { //DS:SI points to 5-note struct // AX = channel // DH = +32 means ignore mute // settings // DH = +128 means to use central // pan and max volume. it_host *chn = &ite->chn[cidx&0xFF]; uint8_t dl = 0; // DL = mask uint8_t al; al = *(data++); if(al != NONOTE) { dl |= 1; chn->Nte = al; } al = *(data++); if(al != 0) { dl |= 2; chn->Ins = al; } al = *(data++); if(al != 0xFF) { dl |= 4; chn->Vol = al; } al = *(data++); uint8_t ah = *(data++); USED(data); if(al != 0 || ah != 0) dl |= 8; chn->Cmd = chn->OCm = al; chn->CVal = chn->OVal = ah; chn->Msk = dl; chn->Flags &= ~(3+32+64+256); chn->Flags |= dh & 0x7F; // Now for command update count chn->CUC = ite->CurrentSpeed; PreInitCommand(ite, chn); if((chn->Flags & 4) != 0 && (dh & 128) != 0) { it_slave *slave = &ite->slave[chn->SCOffst]; slave->Pan = slave->PS = 0x20; // Pan and pan set. slave->CVl = 0x40; // Full channel volume. } ite->DecodeExpectedRow = 0xFFFE; } void Music_PlaySample(it_engine *ite, uint8_t note, uint8_t sidx, uint16_t cidx) { // AL = Note // AH = sample number // CX = channel. it_host *chn = &ite->chn[cidx]; chn->Msk = 3; // Note & Sample chn->Nte = note; chn->Vol = 0xFF; chn->Ins = 0xFF; chn->Nt2 = note; chn->Smp = sidx; chn->Flags |= 0x8020; InitNoCommand(ite, chn); if((chn->Flags & 4) != 0) { it_slave *slave = &ite->slave[chn->SCOffst]; slave->Pan = slave->PS = 0x20; slave->CVl = 0x40; // Full channel volume. slave->NNA = 0; // Note cut. slave->DCT = 0; } chn->Flags &= ~0x8000; ite->DecodeExpectedRow = 0xFFFE; } it_host *Music_GetHostChannelInformationTable(it_engine *ite) { return &ite->chn[0]; } it_slave *Music_GetSlaveChannelInformationTable(it_engine *ite, uint16_t *count) { if(count != NULL) *count = MAXSLAVECHANNELS; return &ite->slave[0]; } void Music_NextOrder(it_engine *ite) { if(ite->PlayMode != 2) return; ite->PlayMode = 0; Music_StopChannels(ite); ite->ProcessRow = 0xFFFE; ite->CurrentTick = 1; ite->RowDelay = 1; ite->PlayMode = 2; } void Music_LastOrder(it_engine *ite) { if(ite->PlayMode != 2) return; if(((int16_t)ite->ProcessOrder) <= 0) return; ite->PlayMode = 0; Music_StopChannels(ite); ite->ProcessOrder = ite->ProcessOrder - 2; ite->ProcessRow = 0xFFFE; ite->CurrentTick = 1; ite->RowDelay = 1; ite->PlayMode = 2; } void Music_SetGlobalVolume(it_engine *ite, uint8_t al) { ite->GlobalVolume = al; RecalculateAllVolumes(ite); } void Music_MuteChannel(it_engine *ite, uint16_t ax) { // AX = channel number uint16_t cx = ite->NumChannels; it_slave *slave = &ite->slave[0]; for(; cx != 0; cx--, slave++) { if((slave->Flags & 1) == 0) continue; if((slave->HCN & 0x7F) != (ax & 0xFF)) continue; slave->Flags |= 0x0840; } } void Music_UnmuteChannel(it_engine *ite, uint16_t ax) { // AX = channel number uint16_t cx = ite->NumChannels; ite->SoloSample = 0xFF; ite->SoloInstrument = 0xFF; it_slave *slave = &ite->slave[0]; for(; cx != 0; cx--, slave++) { if((slave->Flags & 1) == 0) continue; if((slave->HCN & 0x7F) != (ax & 0xFF)) continue; slave->Flags &= ~0x0800; slave->Flags |= 64; } } void Music_ToggleChannel(it_engine *ite, uint16_t ax) { // AX = channel number. if((ite->hdr.Chnl_Vol[ax] & 0x80) != 0) { ite->hdr.Chnl_Vol[ax] &= 0x7F; Music_UnmuteChannel(ite, ax); ite->MuteChannelTable[ax] = 0; } else { // Mute channel ite->MuteChannelTable[ax] ^= 1; ite->hdr.Chnl_Vol[ax] |= 0x80; Music_MuteChannel(ite, ax); } } void Music_UnmuteAll(it_engine *ite) { // solo pressed on already soloed // channel -> turn everything on. uint16_t cx = 64; for(; cx != 0; cx--) if(ite->MuteChannelTable[cx-1] == 1) Music_ToggleChannel(ite, cx-1); } void Music_SoloChannel(it_engine *ite, uint16_t ax) { // AX = channel // Check & count whether any playing. uint8_t *si = &ite->hdr.Chnl_Vol[0]; uint16_t cx = 64; uint16_t dx = 64; for(; cx != 0; cx--) dx -= ((*(si++))>>7)&1; // no way to represent this properly in C --GM // DX = num playing. // check whether it's the current if(dx == 1 && (ite->hdr.Chnl_Vol[ax] & 0x80) == 0) { Music_UnmuteAll(ite); return; } // 64 channel to step through // turn 'em all off. cx = 64; for(; cx != 0; cx--) { if(cx-1 == ax) { if((ite->hdr.Chnl_Vol[cx-1] & 0x80) == 0) Music_ToggleChannel(ite, ax); } else { if((ite->hdr.Chnl_Vol[cx-1] & 0x80) != 0) Music_ToggleChannel(ite, ax); } } } void Music_InitMuteTable(it_engine *ite) { memset(&ite->MuteChannelTable[0], 0, 64); ite->SoloSample = 0xFF; ite->SoloInstrument = 0xFF; ite->AllocateNumChannels = 0; } void Music_InitStereo(it_engine *ite) { ite->d.DriverSetStereo(ite, ite->hdr.Flags & 1); RecalculateAllVolumes(ite); } // note, increase means go faster, not increase number! --GM uint16_t Music_IncreaseSpeed(it_engine *ite) { // Returns AX = speed uint16_t ax = ite->CurrentSpeed; if(ax != 1) { ax--; ite->CurrentSpeed = ax; ite->hdr.IS = ax; } return ax; } uint16_t Music_DecreaseSpeed(it_engine *ite) { // Returns AX = speed uint16_t ax = ite->CurrentSpeed; if(ax != 0xFF) { ax++; ite->CurrentSpeed = ax; ite->hdr.IS = ax; } return ax; } void Music_SetSoundCard(it_engine *ite, uint8_t al) { // AL contains sound card num if(ite->DriverName != NULL) free(ite->DriverName); ite->DriverName = strdup(DriverNameTable[DriverSoundCard[al]]); } void Music_SetSoundCardDriver(it_engine *ite, const char *dssi) { // improvised a bit here --GM if(ite->DriverName != NULL) free(ite->DriverName); ite->DriverName = strdup(dssi); } // exactly why am I doing this stuff? --GM void Music_SetDMA(it_engine *ite, uint8_t al) { ite->d.DMA = al; } void Music_SetMixSpeed(it_engine *ite, uint16_t cx) { ite->d.CmdLineMixSpeed = cx; } void Music_SetIRQ(it_engine *ite, uint16_t cx) { ite->d.IRQ = cx; } void Music_SetAddress(it_engine *ite, uint16_t dx) { ite->d.BasePort = dx; } void Music_GetDisplayVariables(it_engine *ite, uint16_t *CurrentSpeed, uint16_t *Tempo, uint16_t *GlobalVolume) { *CurrentSpeed = ite->CurrentSpeed; *Tempo = ite->Tempo; *GlobalVolume = ite->GlobalVolume; } int16_t Music_AssignSampleToInstrument(it_engine *ite, uint16_t bx) { uint16_t cx; int i; // BX = sample num // returns AX // (returns -1 instead of carry on error --GM) // Check for sample-number's instrument first. // FIXME: probably an off-by-one here --GM it_instrument *ins = &ite->ins[bx]; uint16_t ax = bx+1; if(memcmp(ins, InstrumentHeader, 554)) { // Search cx = 99; ins = &ite->ins[0]; // Points to first inst. ax = 1; for(; cx != 0; cx--, ax++, ins++) if(!memcmp(ins, InstrumentHeader, 554)) break; if(cx == 0) return -1; } #if NETWORKENABLED uint8_t *nsq = Network_GetSendQueue(ite); if(nsq != NULL) { *(nsq++) = 0x00; *(nsq++) = 0x04; *(nsq++) = ax-1; } Network_FinishedSendQueue(ite, nsq); #endif ins = &ite->ins[ax-1]; it_sample *smp = &ite->smp[bx]; memcpy(ins->Name, smp->Name, 26); // Now to fill in table. bx++; for(cx = 120, i = 0; cx != 0; cx--, i++) ins->NoteSamp[i][1] = bx; return ax; } void Music_SetLimit(it_engine *ite, uint16_t cx) { ite->CmdLineNumChannels = cx; } void Music_ReverseChannels(it_engine *ite, uint16_t cx) { USED(cx); ite->d.ReverseChannels = 1; } void Music_IncreaseVolume(it_engine *ite) { if(ite->GlobalVolume < 128) { ite->GlobalVolume++; RecalculateAllVolumes(ite); } } void Music_DecreaseVolume(it_engine *ite) { if(ite->GlobalVolume != 0) { ite->GlobalVolume--; RecalculateAllVolumes(ite); } } void Music_RegetLoopInformation(it_engine *ite) { uint16_t cx; it_slave *slave; for(cx = ite->NumChannels, slave = &ite->slave[0]; cx != 0; cx--, slave++) { if((slave->Flags & 1) == 0) continue; GetLoopInformation(ite, slave); slave->Flags |= 0x40; } } void ResetSoundCardMemory(it_engine *ite) { ite->d.DriverResetMemory(ite); } int Music_SoundCardLoadSample(it_engine *ite, uint16_t sidx) { // AX = sample number // (1 based) // Carry set if insuf mem // incorrect - it's CLEAR on insufficient memory // anyway, we're just returning -1 on fail, 0 on pass --GM if(ite->d.DriverLoadSample(ite, sidx) == -1) { M_Object1List(ite, &O1_OutOfSoundCardMemoryList, 2); return -1; } return 0; } uint16_t Music_SoundCardLoadAllSamples(it_engine *ite) { Music_Stop(ite); S_SaveScreen(ite); S_DrawBox(ite, 3, 30, 50, 28, 30); S_DrawString(ite, 32, 29, PrepareSamplesMsg, 0x20); S_UpdateScreen(ite); ResetSoundCardMemory(ite); uint16_t ax; for(ax = 1; ax <= 100; ax++) if(Music_SoundCardLoadSample(ite, ax) != 0) return 1; S_RestoreScreen(ite); return 1; } int Music_GetFreeSoundCardMemory(it_engine *ite) { // TODO: find out how this works --GM return ite->d.DriverGetStatus(ite); } uint16_t Music_GetNumChannels(it_engine *ite) { return ite->NumChannels; } const uint32_t *Music_GetPitchTable(it_engine *ite) { USED(ite); // Returns ES:DI to pitch table return PitchTable; } void Music_ToggleReverse(it_engine *ite) { ite->d.ReverseChannels ^= 1; RecalculateAllVolumes(ite); SetInfoLine(ite, ReverseMsg); } void Music_PatternStorage(it_engine *ite, uint8_t al) { // this won't mean a damn thing --GM ite->PatternStorage = al; } void Music_InitMixTable(it_engine *ite) { ite->d.DriverSetMixVolume(ite, ite->hdr.MV); // AL = 0->128 } uint16_t Music_GetTempo(it_engine *ite) { // returns in BX --GM return ite->Tempo; } uint16_t Music_GetLastChannel(it_engine *ite) { // Returns AX uint16_t cx = 64; uint16_t ax = 0; uint16_t dx = 0; for(; cx != 0; cx--, dx++) if(((ite->hdr.Chnl_Vol[dx]>>7)^(ite->MuteChannelTable[dx])) == 0) ax = dx; return ax; } int Music_GetDriverScreen(it_engine *ite) { return ite->d.DriverSoundCardScreen(ite); } int Music_GetDriverVariable(it_engine *ite, uint16_t Var) { return ite->d.DriverGetVariable(ite, Var); } int Music_SetDriverVariable(it_engine *ite, uint16_t Var, uint16_t Thing) { return ite->d.DriverSetVariable(ite, Var, Thing); } void Music_SetNextOrder(it_engine *ite, uint16_t order) { ite->ProcessOrder = order-1; } uint16_t Music_GetDelay(it_engine *ite) { // returns in DX --GM // in case you're wondering, // this generates SDx delay effect information for liveplay --GM if(ite->PlayMode == 0) return 0; uint16_t dx = ite->CurrentSpeed; if(dx == ite->CurrentRow) { dx -= ite->ProcessTick; if(dx == 0) return 0; if(dx >= 0x0F) dx = 0x0F; } else { dx--; if(dx >= 0x0F) dx = 0x0F; } dx <<= 8; dx |= 'S'-'@' + 0xD000; return dx; } int InternalTimer(it_engine *ite, uint16_t bx) { // Ticks = (1193181/(2*0.4))/Tempo uint32_t eax = 0x16C214 / (uint32_t)bx; ite->TimerCounter = eax*2; return 0; } int Music_TimeSong(it_engine *ite) { // Time song! S_SaveScreen(ite); S_SetDirectMode(ite, 1); S_DrawSmallBox(ite); S_DrawString(ite, 33, 26, PleaseWaitMsg, 0x20); Music_Stop(ite); /* Mov CX, 0FFFFh Music_TimeSong1: In AL, 21h // Delay mechanism In AL, 0A1h // Delay mechanism Loop Music_TimeSong1 */ ite->StopSong = 0; ite->TotalTimer = 0; ite->TotalTimerHigh = 0; // appears to disable interrupts --GM /* ClI In AL, 0A1h Mov AH, AL In AL, 21h Push AX Mov AL, 0FFh Out 0A1h, AL Out 21h, AL */ int (*OldDriverSetTempo)(it_engine *ite, uint16_t Tempo) = ite->d.DriverSetTempo; ite->d.DriverSetTempo = InternalTimer; uint16_t OldNumChannels = ite->NumChannels; uint16_t OldFlags = ite->hdr.Flags; ite->hdr.Flags &= ~4; Music_PlaySong(ite, 0); for(;;) { uint16_t cx; it_slave *si; uint16_t ax; Update(ite, &cx, &si, &ax); if(ite->StopSong != 0) break; ite->TotalTimer += ite->TimerCounter; if(ite->TotalTimer < ite->TimerCounter) // cheeky way to do AdC 0 --GM ite->TotalTimerHigh++; } Music_Stop(ite); ite->hdr.Flags = OldFlags; ite->NumChannels = OldNumChannels; ite->d.DriverSetTempo = OldDriverSetTempo; /* Pop AX Out 21h, AL Mov AL, AH Out 0A1h, AL StI */ S_SetDirectMode(ite, 0); S_RestoreScreen(ite); M_Object1List(ite, &O1_ShowTime, -1); return 1; } void Music_ShowTime(it_engine *ite) { S_GetDestination(ite); // TODO: work this out --GM uint32_t xtime = (ite->TotalTimerHigh<<16) | (ite->TotalTimer>>16); USED(xtime); D_ShowTime(ite, 43, 27, xtime); } uint16_t Music_GetPatternLength(it_engine *ite) { return ite->NumberOfRows; } uint16_t Music_SaveMIDIConfig(it_engine *ite) { USED(ite); D_GotoStartingDirectory(ite); // TODO! /* Mov AH, 3Ch Xor CX, CX Mov DX, Offset MIDIConfigFileName Int 21h JC Music_SaveMIDIConfig1 Mov BX, AX Mov AH, 40h Mov DS, CS:MIDIDataArea Xor DX, DX Mov CX, (128+16+9)*32 Int 21h Mov AH, 3Eh Int 21h Music_SaveMIDIConfig1: Xor AX, AX */ return 0; } uint8_t *Music_GetMIDIDataArea(it_engine *ite) { USED(ite); // TODO! //Mov DS, CS:MIDIDataArea return NULL; } void Music_ToggleOrderUpdate(it_engine *ite) { const char *msg = OrderUpdateEnabledMsg; ite->OrderLockFlag ^= 1; if(ite->OrderLockFlag != 0) msg = OrderUpdateDisabledMsg; USED(msg); SetInfoLine(ite, msg); } uint16_t Music_ToggleSoloInstrument(it_engine *ite) { return Music_ToggleSolo(ite, SoloInstrumentMsg, &ite->SoloInstrument, 1); } uint16_t Music_ToggleSoloSample(it_engine *ite) { return Music_ToggleSolo(ite, SoloSampleMsg, &ite->SoloSample, 0); } uint16_t Music_ToggleSolo(it_engine *ite, const char *msg, uint8_t *v, uint16_t bp) { uint16_t bx = PE_GetLastInstrument(ite); uint16_t ax = bx; bx += bp; if(*v != (uint8_t)bx) { ite->SoloSample = 0xFF; ite->SoloInstrument = 0xFF; ax++; USED(ax); *v = bx; } else { ite->SoloSample = 0xFF; ite->SoloInstrument = 0xFF; msg = UnsoloMsg; USED(msg); } SetInfoLine(ite, msg); uint16_t cx; it_slave *slave; for(cx = ite->NumChannels, slave = &ite->slave[0]; cx != 0; cx--, slave++) { slave->Flags |= 18; bx = slave->HCN; // BX = channel if((ite->hdr.Chnl_Vol[bx] & 0x80) == 0) slave->Flags &= ~0x800; } RecalculateAllVolumes(ite); return 1; }