void SoundEngine::initStreaming(int timerChannel) { memset(buffer, 0, sizeof buffer); bufferIndex = 0; /* * Next timer tick will populate the first half of the buffer * (bufferIndex=0) but by the time we get there, the audio * hardware will already be started on the second half. We want * the sound hardware and Timer 0 to stay synchronized, so that * they're always operating on opposite halves of 'buffer'. */ timerStart(timerChannel, ClockDivider_256, timerFreqToTicks_256(SAMPLE_RATE / SAMPLES_PER_FRAME), timerCallback); /* * Start playing a circular sound buffer that holds 2 frames. */ SCHANNEL_SOURCE(CHANNEL_PCSPEAKER) = (uint32_t) &buffer[0]; SCHANNEL_REPEAT_POINT(CHANNEL_PCSPEAKER) = 0; SCHANNEL_LENGTH(CHANNEL_PCSPEAKER) = BUFFER_SIZE / sizeof(uint32_t); SCHANNEL_TIMER(CHANNEL_PCSPEAKER) = SOUND_FREQ(SAMPLE_RATE); SCHANNEL_CR(CHANNEL_PCSPEAKER) = SCHANNEL_ENABLE | SOUND_VOL(127) | SOUND_PAN(64) | SOUND_REPEAT | SOUND_FORMAT_8BIT; }
//--------------------------------------------------------------------------------- void startSound(int sampleRate, const void* data, u32 bytes, u8 channel, u8 vol, u8 pan, u8 format) { //--------------------------------------------------------------------------------- SCHANNEL_TIMER(channel) = SOUND_FREQ(sampleRate); SCHANNEL_SOURCE(channel) = (u32)data; SCHANNEL_LENGTH(channel) = bytes >> 2 ; SCHANNEL_CR(channel) = SCHANNEL_ENABLE | SOUND_ONE_SHOT | SOUND_VOL(vol) | SOUND_PAN(pan) | (format==1?SOUND_8BIT:SOUND_16BIT); }
void startSound(int sampleRate, const void* data, uint32 bytes, u8 channel, u8 vol, u8 pan, u8 format) { SCHANNEL_TIMER(channel) = SOUND_FREQ(sampleRate); SCHANNEL_SOURCE(channel) = (u32)data; SCHANNEL_LENGTH(channel) = bytes >> 2; u32 form = 0; switch(format) { case 0: form = SOUND_FORMAT_16BIT; break; case 1: form = SOUND_FORMAT_8BIT; break; case 2: form = SOUND_FORMAT_ADPCM; break; } SCHANNEL_CR(channel) = SCHANNEL_ENABLE | SOUND_ONE_SHOT | SOUND_VOL(vol) | SOUND_PAN(pan) | form; }
// Original FSS Function: Note_On int Track::NoteOn(int key, int vel, int len) { auto sbnk = this->ply->sseq->bank; if (this->patch >= sbnk->instruments.size()) return -1; bool bIsPCM = true; Channel *chn = nullptr; int nCh = -1; auto &instrument = sbnk->instruments[this->patch]; const SBNKInstrumentRange *noteDef = nullptr; int fRecord = instrument.record; if (fRecord == 16) { if (!(instrument.ranges[0].lowNote <= key && key <= instrument.ranges[instrument.ranges.size() - 1].highNote)) return -1; int rn = key - instrument.ranges[0].lowNote; noteDef = &instrument.ranges[rn]; fRecord = noteDef->record; } else if (fRecord == 17) { size_t reg, ranges; for (reg = 0, ranges = instrument.ranges.size(); reg < ranges; ++reg) if (key <= instrument.ranges[reg].highNote) break; if (reg == ranges) return -1; noteDef = &instrument.ranges[reg]; fRecord = noteDef->record; } if (!fRecord) return -1; else if (fRecord == 1) { if (!noteDef) noteDef = &instrument.ranges[0]; } else if (fRecord < 4) { // PSG // fRecord = 2 -> PSG tone, pNoteDef->wavid -> PSG duty // fRecord = 3 -> PSG noise bIsPCM = false; if (!noteDef) noteDef = &instrument.ranges[0]; if (fRecord == 3) { nCh = this->ply->ChannelAlloc(TYPE_NOISE, this->prio); if (nCh < 0) return -1; chn = &this->ply->channels[nCh]; chn->tempReg.CR = SOUND_FORMAT_PSG | SCHANNEL_ENABLE; } else { nCh = this->ply->ChannelAlloc(TYPE_PSG, this->prio); if (nCh < 0) return -1; chn = &this->ply->channels[nCh]; chn->tempReg.CR = SOUND_FORMAT_PSG | SCHANNEL_ENABLE | SOUND_DUTY(noteDef->swav & 0x7); } // TODO: figure out what pNoteDef->tnote means for PSG channels chn->tempReg.TIMER = -SOUND_FREQ(440 * 8); // key #69 (A4) chn->reg.samplePosition = -1; chn->reg.psgX = 0x7FFF; } if (bIsPCM) { nCh = this->ply->ChannelAlloc(TYPE_PCM, this->prio); if (nCh < 0) return -1; chn = &this->ply->channels[nCh]; auto swav = &sbnk->waveArc[noteDef->swar]->swavs.find(noteDef->swav)->second; chn->tempReg.CR = SOUND_FORMAT(swav->waveType & 3) | SOUND_LOOP(!!swav->loop) | SCHANNEL_ENABLE; chn->tempReg.SOURCE = swav; chn->tempReg.TIMER = swav->time; chn->tempReg.REPEAT_POINT = swav->loopOffset; chn->tempReg.LENGTH = swav->nonLoopLength; chn->reg.samplePosition = -3; } chn->state = CS_START; chn->trackId = this->trackId; chn->flags.reset(); chn->prio = this->prio; chn->key = key; chn->orgKey = bIsPCM ? noteDef->noteNumber : 69; chn->velocity = Cnv_Sust(vel); chn->pan = static_cast<int>(noteDef->pan) - 64; chn->modDelayCnt = 0; chn->modCounter = 0; chn->noteLength = len; chn->reg.sampleIncrease = 0; chn->attackLvl = Cnv_Attack(this->a == 0xFF ? noteDef->attackRate : this->a); chn->decayRate = Cnv_Fall(this->d == 0xFF ? noteDef->decayRate : this->d); chn->sustainLvl = this->s == 0xFF ? noteDef->sustainLevel : this->s; chn->releaseRate = Cnv_Fall(this->r == 0xFF ? noteDef->releaseRate : this->r); chn->UpdateVol(*this); chn->UpdatePan(*this); chn->UpdateTune(*this); chn->UpdateMod(*this); chn->UpdatePorta(*this); this->portaKey = key; return nCh; }