/** * Custom audio player. * @param udata User data to send to the player. * @param stream Raw audio to output. * @param len Length of audio to output. */ void AdlibMusic::player(void *udata, Uint8 *stream, int len) { #ifndef __NO_MUSIC if (Options::musicVolume == 0) return; if (Options::musicAlwaysLoop && !func_is_music_playing()) { AdlibMusic *music = (AdlibMusic*)udata; music->play(); return; } while (len != 0) { if (!opl[0] || !opl[1]) return; int i = std::min(delay, len); if (i) { float volume = Game::volumeExponent(Options::musicVolume); YM3812UpdateOne(opl[0], (INT16*)stream, i / 2, 2, volume); YM3812UpdateOne(opl[1], ((INT16*)stream) + 1, i / 2, 2, volume); stream += i; delay -= i; len -= i; } if (!len) return; func_play_tick(); delay = delayRates[rate]; } #endif }
void CEmuopl::update(short *buf, int samples) { int i; if(use16bit) { YM3812UpdateOne(0,buf,samples); if(stereo) for(i=samples-1;i>=0;i--) { buf[i*2] = buf[i]; buf[i*2+1] = buf[i]; } } else { short *tempbuf = new short[stereo ? samples*2 : samples]; int i; YM3812UpdateOne(0,tempbuf,samples); if(stereo) for(i=samples-1;i>=0;i--) { tempbuf[i*2] = tempbuf[i]; tempbuf[i*2+1] = tempbuf[i]; } for(i=0;i<(stereo ? samples*2 : samples);i++) ((char *)buf)[i] = (tempbuf[i] >> 8) ^ 0x80; delete [] tempbuf; } }
// Drop-in replacement for id_sd.c:alOut void BE_ST_ALOut(uint8_t reg,uint8_t val) { BE_ST_LockAudioRecursively(); // RECURSIVE lock // FIXME: The original code for alOut adds 6 reads of the // register port after writing to it (3.3 microseconds), and // then 35 more reads of register port after writing to the // data port (23 microseconds). // // It is apparently important for a portion of the fuse // breakage sound at the least. For now a hack is implied. YM3812Write(&oplChip, reg, val); // Hack comes with a "magic number" // that appears to make it work better unsigned int length = OPL_SAMPLE_RATE / 10000; if (length > OPL_NUM_OF_SAMPLES - g_sdlALOutSamplesEnd) { BE_Cross_LogMessage(BE_LOG_MSG_WARNING, "BE_ST_ALOut overflow, want %u, have %u\n", length, OPL_NUM_OF_SAMPLES - g_sdlALOutSamplesEnd); // FIXME - Other thread length = OPL_NUM_OF_SAMPLES - g_sdlALOutSamplesEnd; } if (length) { YM3812UpdateOne(&oplChip, &g_sdlALOutSamples[g_sdlALOutSamplesEnd], length); g_sdlALOutSamplesEnd += length; } BE_ST_UnlockAudioRecursively(); // RECURSIVE unlock }
int AdLibMusic::readBuffer(int16 *data, const int numSamples) { if (_musicData == NULL) { // no music loaded memset(data, 0, numSamples * sizeof(int16)); } else if ((_currentMusic == 0) || (_numberOfChannels == 0)) { // music loaded but not played as of yet memset(data, 0, numSamples * sizeof(int16)); // poll anyways as pollMusic() can activate the music pollMusic(); _nextMusicPoll = _sampleRate / 50; } else { uint32 render; uint remaining = numSamples; while (remaining) { render = (remaining > _nextMusicPoll) ? _nextMusicPoll : remaining; remaining -= render; _nextMusicPoll -= render; YM3812UpdateOne(_opl, data, render); data += render; if (_nextMusicPoll == 0) { pollMusic(); _nextMusicPoll = _sampleRate / 50; } } } return numSamples; }
static void YM3812Render(int nSegmentLength) { if (nYM3812Position >= nSegmentLength) { return; } // bprintf(PRINT_NORMAL, _T(" YM3812 render %6i -> %6i\n", nYM3812Position, nSegmentLength)); nSegmentLength -= nYM3812Position; YM3812UpdateOne(0, pBuffer + 0 * 4096 + 4 + nYM3812Position, nSegmentLength); nYM3812Position += nSegmentLength; }
static void YM3812Render(INT32 nSegmentLength) { #if defined FBA_DEBUG if (!DebugSnd_YM3812Initted) bprintf(PRINT_ERROR, _T("YM3812Render called without init\n")); #endif if (nYM3812Position >= nSegmentLength) { return; } // bprintf(PRINT_NORMAL, _T(" YM3812 render %6i -> %6i\n", nYM3812Position, nSegmentLength)); nSegmentLength -= nYM3812Position; YM3812UpdateOne(0, pBuffer + 0 * 4096 + 4 + nYM3812Position, nSegmentLength); nYM3812Position += nSegmentLength; }
static void ym3812_stream_update(void *param, stream_sample_t **inputs, stream_sample_t **buffer, int length) { struct ym3812_info *info = param; YM3812UpdateOne(info->chip, buffer[0], length); }
void opl_update( OPLSAMPLE *buf, int samples ) { YM3812UpdateOne(opl, buf, samples); }
bool OPLmusicBlock::ServiceStream (void *buff, int numbytes) { short *samples = (short *)buff; int *samples1 = SampleBuff; int numsamples = numbytes / 2; bool prevEnded = false; bool res = true; memset (SampleBuff, 0, numsamples * 4); #ifdef _WIN32 EnterCriticalSection (&ChipAccess); #else if (SDL_mutexP (ChipAccess) != 0) return true; #endif while (numsamples > 0) { int samplesleft = MIN (numsamples, NextTickIn); if (samplesleft > 0) { YM3812UpdateOne (0, samples1, samplesleft); if (TwoChips) { YM3812UpdateOne (1, samples1, samplesleft); } NextTickIn -= samplesleft; assert (NextTickIn >= 0); numsamples -= samplesleft; samples1 += samplesleft; } if (NextTickIn == 0) { int next = PlayTick (); if (next == 0) { // end of song if (!Looping || prevEnded) { if (numsamples > 0) { YM3812UpdateOne (0, samples1, numsamples); if (TwoChips) { YM3812UpdateOne (1, samples1, numsamples); } } res = false; break; } else { // Avoid infinite loops from songs that do nothing but end prevEnded = true; Restart (); } } else { prevEnded = false; NextTickIn = SamplesPerTick * next; assert (NextTickIn >= 0); MLtime += next; } } } #ifdef _WIN32 LeaveCriticalSection (&ChipAccess); #else SDL_mutexV (ChipAccess); #endif numsamples = numbytes / 2; samples1 = SampleBuff; #if defined(_MSC_VER) && defined(USEASM) if (CPU.bCMOV && numsamples > 1) { __asm { mov ecx, numsamples mov esi, samples1 mov edi, samples shr ecx, 1 lea esi, [esi+ecx*8] lea edi, [edi+ecx*4] neg ecx mov edx, 0x00007fff looper: mov eax, [esi+ecx*8] mov ebx, [esi+ecx*8+4] // Shift the samples down to reduce the chance of clipping at the high end. // Since most of the waveforms we produce only use the upper half of the // sine wave, this is a good thing. Although it does leave less room at // the bottom of the sine before clipping, almost none of the songs I tested // went below -9000. sub eax, 18000 sub ebx, 18000 // Clamp high cmp eax, edx cmovg eax, edx cmp ebx, edx cmovg ebx, edx // Clamp low not edx cmp eax, edx cmovl eax, edx cmp ebx, edx cmovl ebx, edx not edx mov [edi+ecx*4], ax mov [edi+ecx*4+2], bx inc ecx jnz looper test numsamples, 1 jz done mov eax, [esi+ecx*8] sub eax, 18000 cmp eax, edx cmovg eax, edx not edx cmp eax, edx cmovl eax, edx mov [edi+ecx*2], ax done: } }
void SDL_IMFMusicPlayer(void *udata, Uint8 *stream, int len) { int stereolen = len>>1; int sampleslen = stereolen>>1; Sint16 *stream16 = (Sint16 *) (void *) stream; // expect correct alignment while(1) { if(numreadysamples) { if(numreadysamples<sampleslen) { if(MusicMode == smm_AdLib || SoundMode == sdm_AdLib) YM3812UpdateOne(oplChip, stream16, numreadysamples); // Mix the emulated PC sounds into the AdLib buffer: SDL_PCEmulateAndMix(stream16, numreadysamples); stream16 += numreadysamples*2; sampleslen -= numreadysamples; } else { if(MusicMode == smm_AdLib || SoundMode == sdm_AdLib) YM3812UpdateOne(oplChip, stream16, sampleslen); // Mix the emulated PC sounds into the AdLib buffer: SDL_PCEmulateAndMix(stream16, sampleslen); numreadysamples -= sampleslen; return; } } SDL_LockMutex(audioMutex); soundTimeCounter--; if(!soundTimeCounter) { // Sound effects are played at 140 Hz (every 5 cycles of the 700 Hz music service) soundTimeCounter = SOUND_TICKS; SDL_PCService(); // THIS is the way the original Wolfenstein 3-D code handled it! if(alSound) { if(*alSound) { alOut(alFreqL, *alSound); alOut(alFreqH, alBlock); } else alOut(alFreqH, 0); alSound++; if (!(--alLengthLeft)) { alSound = 0; SoundPriority=0; alOut(alFreqH, 0); } } } if(sqActive) { do { if(sqHackTime > alTimeCount) break; sqHackTime = alTimeCount + LittleShort(*(sqHackPtr+1)); alOutMusic(*(byte *) sqHackPtr, *(((byte *) sqHackPtr)+1)); sqHackPtr += 2; sqHackLen -= 4; } while(sqHackLen>0); alTimeCount++; if(!sqHackLen) { sqHackPtr = sqHack; sqHackLen = sqHackSeqLen; sqHackTime = 0; alTimeCount = 0; } } numreadysamples = samplesPerMusicTick; SDL_UnlockMutex(audioMutex); } }
void AdLibMidiDriver::generateSamples(int16 *data, int len) { memset(data, 0, sizeof(int16) * len); YM3812UpdateOne(_opl, data, len); }
static void SD_IMFMusicPlayer(void *udata, Uint8 *stream, int len) { int stereolen = len>>1; int sampleslen = stereolen>>1; INT16 *stream16 = (INT16 *) (void *) stream; /* expect correct alignment */ while(1) { if(numreadysamples) { if(numreadysamples < sampleslen) { YM3812UpdateOne(0, stream16, numreadysamples); stream16 += numreadysamples*2; sampleslen -= numreadysamples; } else { YM3812UpdateOne(0, stream16, sampleslen); numreadysamples -= sampleslen; return; } } soundTimeCounter--; if(!soundTimeCounter) { soundTimeCounter = 5; if(curAlSound != alSound) { curAlSound = curAlSoundPtr = alSound; curAlLengthLeft = alLengthLeft; } if(curAlSound) { if(*curAlSoundPtr) { YM3812Write(0, alFreqL, *curAlSoundPtr); YM3812Write(0, alFreqH, alBlock); } else YM3812Write(0, alFreqH, 0); curAlSoundPtr++; curAlLengthLeft--; if(!curAlLengthLeft) { curAlSound = alSound = 0; SoundNumber = (soundnames) 0; SoundPriority = 0; YM3812Write(0, alFreqH, 0); } } } if(sqActive) { do { if(sqHackTime > alTimeCount) break; sqHackTime = alTimeCount + (word)Retro_SwapLES16(*(sqHackPtr+1)); YM3812Write(0, *(byte *) sqHackPtr, *(((byte *) sqHackPtr)+1)); sqHackPtr += 2; sqHackLen -= 4; } while(sqHackLen>0); alTimeCount++; if(!sqHackLen) { sqHackPtr = sqHack; sqHackLen = sqHackSeqLen; sqHackTime = 0; alTimeCount = 0; } } numreadysamples = samplesPerMusicTick; } }
void CEmuopl::update(short *buf, int samples) { int i; //ensure that our mix buffers are adequately sized if(unlikely (mixbufSamples < samples)) { if (mixbuf0) { delete[] mixbuf0; mixbuf0 = 0; } if (mixbuf1) { delete[] mixbuf1; mixbuf1 = 0; } //*2 = make room for stereo, if we need it mixbuf0 = new short[samples*2]; mixbuf1 = new short[samples*2]; } mixbufSamples = samples; //data should be rendered to outbuf //tempbuf should be used as a temporary buffer //if we are supposed to generate 16bit output, //then outbuf may point directly to the actual waveform output "buf" //if we are supposed to generate 8bit output, //then outbuf cannot point to "buf" (because there will not be enough room) //and so it must point to a mixbuf instead-- //it will be reduced to 8bit and put in "buf" later short *outbuf; if (likely (use16bit)) { outbuf = buf; } else { outbuf = mixbuf1; } //...there is a potentially confusing situation where mixbuf1 can be aliased. //beware. it is a little loony. //all of the following rendering code produces 16bit output switch(currType) { case TYPE_OPL2: //for opl2 mode: //render chip0 to the output buffer YM3812UpdateOne(opl[0],outbuf,samples); //if we are supposed to output stereo, //then we need to dup the mono channel if(unlikely (stereo)) { for(i=samples-1;i>=0;i--) { outbuf[i*2] = outbuf[i]; outbuf[i*2+1] = outbuf[i]; } } break; case TYPE_OPL3: // unsupported break; case TYPE_DUAL_OPL2: //for dual opl2 mode: //render each chip to a different tempbuffer YM3812UpdateOne(opl[0],mixbuf1,samples); YM3812UpdateOne(opl[1],mixbuf0,samples); //output stereo: //then we need to interleave the two buffers if(unlikely (stereo)){ //first, spread tempbuf's samples across left channel for(i=0;i<samples;i++) outbuf[i*2] = mixbuf1[i]; //next, insert the samples from tempbuf2 into right channel for(i=0;i<samples;i++) outbuf[i*2+1] = mixbuf0[i]; } else { //output mono: //then we need to mix the two buffers into buf for(i=0;i<samples;i++) { int sample = (int)mixbuf0[i] + (int)mixbuf1[i]; if (unlikely (sample > 32767)) { sample = 32767; } else if (unlikely (sample < -32768)) { sample = -32768; } outbuf[i] = (short)sample; } } break; } //now reduce to 8bit if we need to if(unlikely (!use16bit)) { int smp = stereo ? samples*2 : samples; for(i=0;i<smp;i++) { ((char *)buf)[i] = (outbuf[i] >> 8) ^ 0x80; } }