// Simple AMIGA Paula Audio channel mixer -- Copyright (C) Michael Schwendt // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // History: This once was a simple mixer (it still is ;) that was // used in a private MOD/TFMX player and was configured at run-time // to mix up to 32 individual audio channels. In this particular // version, 16-bit and mono are moved back in. Most of that old code // has not been touched though because it has done its job well even // if it looks quite ugly. So, please bear with me, if you find things // in here that are not used by the FC player. #include "LamePaula.h" LamePaulaMixer::LamePaulaMixer() : AMIGA_CLOCK(AMIGA_CLOCK_PAL) { // Start with no voice ptrs. _voices = 0; for (ubyte v=0; v<_maxVoices; v++) { _voice[v] = 0; } } LamePaulaMixer::~LamePaulaMixer() { end(); } void LamePaulaMixer::end() { for (ubyte v=0; v<_voices; v++) { delete _voice[v]; _voice[v] = 0; } _voices = 0; } PaulaVoice* LamePaulaMixer::getVoice(ubyte n) { if (n < _maxVoices) { return _voice[n]; } else { return 0; } } void LamePaulaMixer::init(ubyte voices) { if ((voices <= _maxVoices) && (voices != _voices)) { end(); _voices = voices; for (ubyte v=0; v<_voices; v++) { _voice[v] = new LamePaulaVoice; } } for (ubyte v=0; v<_voices; v++) { LamePaulaVoice* pv = _voice[v]; pv->start = &emptySample; pv->end = &emptySample+1; pv->repeatStart = &emptySample; pv->repeatEnd = &emptySample+1; pv->length = 1; pv->curPeriod = 0; pv->stepSpeed = 0; pv->stepSpeedPnt = 0; pv->stepSpeedAddPnt = 0; pv->off(); } } void LamePaulaMixer::init(udword freq, ubyte bits, ubyte channels, uword zero) { _pcmFreq = freq; _bitsPerSample = bits; _channels = channels; _zero = zero; setReplayingSpeed(); bufferScale = 0; toFill = 0; if (bits == 8) { zero8bit = zero; if (channels == 1) { _fillFunc = &LamePaulaMixer::fill8bitMono; } else { // if (channels == 2) _fillFunc = &LamePaulaMixer::fill8bitStereo; ++bufferScale; } } else { // if (bits == 16) zero16bit = zero; ++bufferScale; if (channels == 1) { _fillFunc = &LamePaulaMixer::fill16bitMono; } else { // if (channels == 2) _fillFunc = &LamePaulaMixer::fill16bitStereo; ++bufferScale; } } uword ui; long si; ubyte voicesPerChannel = _voices/_channels; // Input samples: 80,81,82,...,FE,FF,00,01,02,...,7E,7F // Array: 00/x, 01/x, 02/x,...,7E/x,7F/x,80/x,81/x,82/x,...,FE/x/,FF/x ui = 0; si = 0; while (si++ < 128) { mix8[ui++] = (sbyte)(si/voicesPerChannel); } si = -128; while (si++ < 0) { mix8[ui++] = (sbyte)(si/voicesPerChannel); } // Input samples: 80,81,82,...,FE,FF,00,01,02,...,7E,7F // Array: 0/x, 100/x, 200/x, ..., FF00/x ui = 0; si = 0; while (si < 128*256) { mix16[ui++] = (sword)(si/voicesPerChannel); si += 256; } si = -128*256; while (si < 0) { mix16[ui++] = (sword)(si/voicesPerChannel); si += 256; } } void LamePaulaMixer::setReplayingSpeed() { samples = ( samplesOrg = _pcmFreq / 50 ); samplesPnt = (( _pcmFreq % 50 ) * 65536 ) / 50; samplesAdd = 0; } void LamePaulaMixer::setBpm(uword bpm) { uword callsPerSecond = (bpm * 2) / 5; samples = ( samplesOrg = _pcmFreq / callsPerSecond ); samplesPnt = (( _pcmFreq % callsPerSecond ) * 65536 ) / callsPerSecond; samplesAdd = 0; } void LamePaulaMixer::fillBuffer(void* buffer, udword bufferLen, PaulaPlayer *player) { // Both, 16-bit and stereo samples take more memory. // Hence fewer samples fit into the buffer. bufferLen >>= bufferScale; while ( bufferLen > 0 ) { if ( toFill > bufferLen ) { buffer = (this->*_fillFunc)(buffer, bufferLen); toFill -= bufferLen; bufferLen = 0; } else if ( toFill > 0 ) { buffer = (this->*_fillFunc)(buffer, toFill); bufferLen -= toFill; toFill = 0; } if ( toFill == 0 ) { player->run(); udword temp = ( samplesAdd += samplesPnt ); samplesAdd = temp & 0xFFFF; toFill = samples + ( temp > 65535 ); for (ubyte v=0; v<_voices; v++) { LamePaulaVoice *pv = _voice[v]; if ( pv->paula.period != pv->curPeriod ) { pv->curPeriod = pv->paula.period; if (pv->curPeriod != 0) { pv->stepSpeed = (AMIGA_CLOCK/_pcmFreq) / pv->curPeriod; pv->stepSpeedPnt = (((AMIGA_CLOCK/_pcmFreq) % pv->curPeriod ) * 65536 ) / pv->curPeriod; } else { pv->stepSpeed = pv->stepSpeedPnt = 0; } } } // for voices } } // while bufferLen } void* LamePaulaMixer::fill8bitMono(void* buffer, udword numberOfSamples) { ubyte* buffer8bit = static_cast(buffer); for (ubyte v=0; v<_voices; v++) { buffer8bit = static_cast(buffer); LamePaulaVoice *pv = _voice[v]; for (udword n = numberOfSamples; n>0; n--) { if (v == 0) { *buffer8bit = zero8bit; } pv->stepSpeedAddPnt += pv->stepSpeedPnt; pv->start += ( pv->stepSpeed + ( pv->stepSpeedAddPnt > 65535 ) ); pv->stepSpeedAddPnt &= 65535; if ( pv->start < pv->end ) { *buffer8bit += ( pv->paula.volume * mix8[*pv->start] ) >> 6; } else { if ( pv->looping ) { pv->start = pv->repeatStart; pv->end = pv->repeatEnd; if ( pv->start < pv->end ) { *buffer8bit += ( pv->paula.volume * mix8[*pv->start] ) >> 6; } } } buffer8bit++; } } return(buffer8bit); } void* LamePaulaMixer::fill8bitStereo( void* buffer, udword numberOfSamples ) { ubyte* buffer8bit = (static_cast(buffer))+1; for (ubyte v=1; v<_voices; v+=2) { buffer8bit = (static_cast(buffer))+1; LamePaulaVoice *pv = _voice[v]; for (udword n = numberOfSamples; n>0; n--) { if (v == 1) { *buffer8bit = zero8bit; } pv->stepSpeedAddPnt += pv->stepSpeedPnt; pv->start += ( pv->stepSpeed + ( pv->stepSpeedAddPnt > 65535 ) ); pv->stepSpeedAddPnt &= 65535; if ( pv->start < pv->end ) { *buffer8bit += ( pv->paula.volume * mix8[*pv->start] ) >> 6; } else { if ( pv->looping ) { pv->start = pv->repeatStart; pv->end = pv->repeatEnd; if ( pv->start < pv->end ) { *buffer8bit += ( pv->paula.volume * mix8[*pv->start] ) >> 6; } } } buffer8bit += 2; } } buffer8bit = static_cast(buffer); for (ubyte v=0; v<_voices; v+=2) { buffer8bit = static_cast(buffer); LamePaulaVoice *pv = _voice[v]; for (udword n = numberOfSamples; n>0; n--) { if (v == 0) { *buffer8bit = zero8bit; } pv->stepSpeedAddPnt += pv->stepSpeedPnt; pv->start += ( pv->stepSpeed + ( pv->stepSpeedAddPnt > 65535 ) ); pv->stepSpeedAddPnt &= 65535; if ( pv->start < pv->end ) { *buffer8bit += ( pv->paula.volume * mix8[*pv->start] ) >> 6; } else { if ( pv->looping ) { pv->start = pv->repeatStart; pv->end = pv->repeatEnd; if ( pv->start < pv->end ) { *buffer8bit += ( pv->paula.volume * mix8[*pv->start] ) >> 6; } } } buffer8bit += 2; } } return(buffer8bit); } void* LamePaulaMixer::fill16bitMono( void* buffer, udword numberOfSamples ) { sword* buffer16bit = static_cast(buffer); for (ubyte v=0; v<_voices; v++) { buffer16bit = static_cast(buffer); LamePaulaVoice *pv = _voice[v]; for (udword n = numberOfSamples; n>0; n--) { if (v == 0) { *buffer16bit = zero16bit; } pv->stepSpeedAddPnt += pv->stepSpeedPnt; pv->start += ( pv->stepSpeed + ( pv->stepSpeedAddPnt > 65535 ) ); pv->stepSpeedAddPnt &= 65535; if ( pv->start < pv->end ) { *buffer16bit += ( pv->paula.volume * mix16[*pv->start] ) >> 6; } else { if ( pv->looping ) { pv->start = pv->repeatStart; pv->end = pv->repeatEnd; if ( pv->start < pv->end ) { *buffer16bit += ( pv->paula.volume * mix16[*pv->start] ) >> 6; } } } buffer16bit++; } } return(buffer16bit); } void* LamePaulaMixer::fill16bitStereo( void *buffer, udword numberOfSamples ) { sword* buffer16bit = (static_cast(buffer))+1; for (ubyte v=1; v<_voices; v+=2 ) { buffer16bit = (static_cast(buffer))+1; LamePaulaVoice *pv = _voice[v]; for (udword n = numberOfSamples; n>0; n--) { if (v == 1) { *buffer16bit = zero16bit; } pv->stepSpeedAddPnt += pv->stepSpeedPnt; pv->start += ( pv->stepSpeed + ( pv->stepSpeedAddPnt > 65535 ) ); pv->stepSpeedAddPnt &= 65535; if ( pv->start < pv->end ) { *buffer16bit += ( pv->paula.volume * mix16[*pv->start] ) >> 6; } else { if ( pv->looping ) { pv->start = pv->repeatStart; pv->end = pv->repeatEnd; if ( pv->start < pv->end ) { *buffer16bit += ( pv->paula.volume * mix16[*pv->start] ) >> 6; } } } buffer16bit += 2; } } buffer16bit = static_cast(buffer); for (ubyte v=0; v<_voices; v+=2 ) { buffer16bit = static_cast(buffer); LamePaulaVoice *pv = _voice[v]; for (udword n = numberOfSamples; n>0; n--) { if (v == 0) { *buffer16bit = zero16bit; } pv->stepSpeedAddPnt += pv->stepSpeedPnt; pv->start += ( pv->stepSpeed + ( pv->stepSpeedAddPnt > 65535 ) ); pv->stepSpeedAddPnt &= 65535; if ( pv->start < pv->end ) { *buffer16bit += ( pv->paula.volume * mix16[*pv->start] ) >> 6; } else { if ( pv->looping ) { pv->start = pv->repeatStart; pv->end = pv->repeatEnd; if ( pv->start < pv->end ) { *buffer16bit += ( pv->paula.volume * mix16[*pv->start] ) >> 6; } } } buffer16bit += 2; } } return(buffer16bit); }