Refactor CD-DA flow by removing intermediate buffers and loops
Thanks to @ripsaw8080 for insight into CD-DA channel mapping, @hail-to-the-ryzen for testing and flagging a position-tracking bug, and @dreamer_ for guidance and code review. The CD-DA volume and channel mapping loops were moved to generic mixer calls and no longer require a pre-processing loop: - Application-controlled CD-DA volume adjustment is now applied using an existing mixer volume scalar that was previously unused by the CD-DA code. - Mapping of CD-DA left and right channels is now applied at the tail end of the mixer's sample ingest sequence. The following have been removed: - The CD-DA callback chunk-wise circular buffer - The decode buffers in the Opus and MP3 decoders - The decode buffer and conversion buffers in SDL_Sound These removals and API changes allow the image player's buffer to be passed-through ultimately to the audio codec, skipping multiple intermediate buffers.
This commit is contained in:
parent
5a9dd2866b
commit
d1a6f373cb
5 changed files with 310 additions and 336 deletions
|
@ -46,8 +46,11 @@ extern Bit8u MixTemp[MIXER_BUFSIZE];
|
|||
|
||||
class MixerChannel {
|
||||
public:
|
||||
MixerChannel(MIXER_Handler _handler, Bitu _freq, const char * _name);
|
||||
void SetVolume(float _left,float _right);
|
||||
void SetScale( float f );
|
||||
void SetScale(float f);
|
||||
void SetScale(float _left, float _right);
|
||||
void MapChannels(Bit8u _left, Bit8u _right);
|
||||
void UpdateVolume(void);
|
||||
void SetFreq(Bitu _freq);
|
||||
void Mix(Bitu _needed);
|
||||
|
@ -77,24 +80,25 @@ public:
|
|||
|
||||
void FillUp(void);
|
||||
void Enable(bool _yesno);
|
||||
MIXER_Handler handler;
|
||||
float volmain[2];
|
||||
float scale;
|
||||
Bit32s volmul[2];
|
||||
|
||||
//This gets added the frequency counter each mixer step
|
||||
Bitu freq_add;
|
||||
//When this flows over a new sample needs to be read from the device
|
||||
Bitu freq_counter;
|
||||
//Timing on how many samples have been done and were needed by th emixer
|
||||
Bitu done, needed;
|
||||
//Previous and next samples
|
||||
Bits prevSample[2];
|
||||
Bits nextSample[2];
|
||||
const char * name;
|
||||
bool interpolate;
|
||||
bool enabled;
|
||||
MixerChannel * next;
|
||||
|
||||
float volmain[2];
|
||||
MixerChannel* next;
|
||||
const char* name;
|
||||
Bitu done; //Timing on how many samples have been done by the mixer
|
||||
bool enabled;
|
||||
|
||||
private:
|
||||
MixerChannel();
|
||||
MIXER_Handler handler;
|
||||
Bitu freq_add; //This gets added the frequency counter each mixer step
|
||||
Bitu freq_counter; //When this flows over a new sample needs to be read from the device
|
||||
Bitu needed; //Timing on how many samples were needed by the mixer
|
||||
Bits prev_sample[2]; //Previous and next samples
|
||||
Bits next_sample[2];
|
||||
Bit32s volmul[2];
|
||||
float scale[2];
|
||||
Bit8u channel_map[2]; //Output channel mapping
|
||||
bool interpolate;
|
||||
};
|
||||
|
||||
MixerChannel * MIXER_AddChannel(MIXER_Handler handler,Bitu freq,const char * name);
|
||||
|
|
|
@ -36,13 +36,11 @@
|
|||
#include "mixer.h"
|
||||
#include "../libs/decoders/SDL_sound.h"
|
||||
|
||||
#define RAW_SECTOR_SIZE 2352
|
||||
#define COOKED_SECTOR_SIZE 2048
|
||||
#define AUDIO_DECODE_BUFFER_SIZE 16512
|
||||
// 16512 is 16384 + 128, which enough for four 4KB decode audio chunks plus 128 bytes extra,
|
||||
// which accomodate the leftovers from typically callbacks, which also helps minimize
|
||||
// most of the time. This size is also an even multiple of 8-bytes, allowing the compiler to
|
||||
// use "large-D" SIMD instructions on it.
|
||||
// CDROM data and audio format constants
|
||||
#define RAW_SECTOR_SIZE 2352
|
||||
#define COOKED_SECTOR_SIZE 2048
|
||||
#define BYTES_PER_TRACK_FRAME 4
|
||||
#define REDBOOK_FRAMES_PER_SECOND 75
|
||||
|
||||
enum { CDROM_USE_SDL, CDROM_USE_ASPI, CDROM_USE_IOCTL_DIO, CDROM_USE_IOCTL_DX, CDROM_USE_IOCTL_MCI };
|
||||
|
||||
|
@ -53,17 +51,16 @@ typedef struct SMSF {
|
|||
} TMSF;
|
||||
|
||||
typedef struct SCtrl {
|
||||
Bit8u out[4]; // output channel
|
||||
Bit8u vol[4]; // channel volume
|
||||
Bit8u out[4]; // output channel mapping
|
||||
Bit8u vol[4]; // channel volume (0 to 255)
|
||||
} TCtrl;
|
||||
|
||||
// Conversion function from frames to Minutes/Second/Frames
|
||||
//
|
||||
template<typename T>
|
||||
inline void frames_to_msf(int frames, T *m, T *s, T *f) {
|
||||
const int cd_fps = 75;
|
||||
*f = frames % cd_fps;
|
||||
frames /= cd_fps;
|
||||
*f = frames % REDBOOK_FRAMES_PER_SECOND;
|
||||
frames /= REDBOOK_FRAMES_PER_SECOND;
|
||||
*s = frames % 60;
|
||||
frames /= 60;
|
||||
*m = frames;
|
||||
|
@ -72,8 +69,7 @@ inline void frames_to_msf(int frames, T *m, T *s, T *f) {
|
|||
// Conversion function from Minutes/Second/Frames to frames
|
||||
//
|
||||
inline int msf_to_frames(int m, int s, int f) {
|
||||
const int cd_fps = 75;
|
||||
return m * 60 * cd_fps + s * cd_fps + f;
|
||||
return m * 60 * REDBOOK_FRAMES_PER_SECOND + s * REDBOOK_FRAMES_PER_SECOND + f;
|
||||
}
|
||||
|
||||
extern int CDROM_GetMountType(char* path, int force);
|
||||
|
@ -161,7 +157,7 @@ private:
|
|||
public:
|
||||
virtual bool read(Bit8u *buffer, int seek, int count) = 0;
|
||||
virtual bool seek(Bit32u offset) = 0;
|
||||
virtual Bit16u decode(Bit8u *buffer) = 0;
|
||||
virtual Bit32u decode(Bit16s *buffer, Bit32u desired_track_frames) = 0;
|
||||
virtual Bit16u getEndian() = 0;
|
||||
virtual Bit32u getRate() = 0;
|
||||
virtual Bit8u getChannels() = 0;
|
||||
|
@ -175,7 +171,7 @@ private:
|
|||
BinaryFile (const char *filename, bool &error);
|
||||
bool read(Bit8u *buffer, int seek, int count);
|
||||
bool seek(Bit32u offset);
|
||||
Bit16u decode(Bit8u *buffer);
|
||||
Bit32u decode(Bit16s *buffer, Bit32u desired_track_frames);
|
||||
Bit16u getEndian();
|
||||
Bit32u getRate() { return 44100; }
|
||||
Bit8u getChannels() { return 2; }
|
||||
|
@ -191,7 +187,7 @@ private:
|
|||
AudioFile (const char *filename, bool &error);
|
||||
bool read(Bit8u *buffer, int seek, int count) { return false; }
|
||||
bool seek(Bit32u offset);
|
||||
Bit16u decode(Bit8u *buffer);
|
||||
Bit32u decode(Bit16s *buffer, Bit32u desired_track_frames);
|
||||
Bit16u getEndian();
|
||||
Bit32u getRate();
|
||||
Bit8u getChannels();
|
||||
|
@ -237,26 +233,21 @@ static CDROM_Interface_Image* images[26];
|
|||
|
||||
private:
|
||||
// player
|
||||
static void CDAudioCallBack(Bitu len);
|
||||
static void CDAudioCallBack(Bitu desired_frames);
|
||||
int GetTrack(int sector);
|
||||
|
||||
static struct imagePlayer {
|
||||
Bit8u buffer[AUDIO_DECODE_BUFFER_SIZE];
|
||||
TCtrl ctrlData;
|
||||
Bit16s buffer[MIXER_BUFSIZE * 2]; // 2 channels (max)
|
||||
TrackFile *trackFile;
|
||||
MixerChannel *channel;
|
||||
CDROM_Interface_Image *cd;
|
||||
void (MixerChannel::*addSamples) (Bitu, const Bit16s*);
|
||||
Bit32u startFrame;
|
||||
Bit32u currFrame;
|
||||
Bit32u numFrames;
|
||||
Bit32u playbackTotal;
|
||||
Bit32s playbackRemaining;
|
||||
Bit16u bufferPos;
|
||||
Bit16u bufferConsumed;
|
||||
void (MixerChannel::*addFrames) (Bitu, const Bit16s*);
|
||||
Bit32u startRedbookFrame;
|
||||
Bit32u totalRedbookFrames;
|
||||
Bit32u playedTrackFrames;
|
||||
Bit32u totalTrackFrames;
|
||||
bool isPlaying;
|
||||
bool isPaused;
|
||||
bool ctrlUsed;
|
||||
} player;
|
||||
|
||||
void ClearTracks();
|
||||
|
@ -407,7 +398,7 @@ private:
|
|||
SDL_mutex *mutex;
|
||||
Bit8u buffer[8192];
|
||||
int bufLen;
|
||||
int currFrame;
|
||||
int currFrame;
|
||||
int targetFrame;
|
||||
bool isPlaying;
|
||||
bool isPaused;
|
||||
|
|
|
@ -27,14 +27,9 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <limits.h> //GCC 2.95
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <sys/stat.h>
|
||||
#include "cdrom.h"
|
||||
#include "drives.h"
|
||||
#include "support.h"
|
||||
#include "setup.h"
|
||||
|
||||
#if !defined(WIN32)
|
||||
#include <libgen.h>
|
||||
|
@ -42,6 +37,11 @@
|
|||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "cdrom.h"
|
||||
#include "drives.h"
|
||||
#include "support.h"
|
||||
#include "setup.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define MAX_LINE_LENGTH 512
|
||||
|
@ -96,12 +96,8 @@ int CDROM_Interface_Image::BinaryFile::getLength()
|
|||
|
||||
Bit16u CDROM_Interface_Image::BinaryFile::getEndian()
|
||||
{
|
||||
// Image files are read into native-endian byte-order
|
||||
#if defined(WORDS_BIGENDIAN)
|
||||
return AUDIO_S16MSB;
|
||||
#else
|
||||
// Image files are always little endian
|
||||
return AUDIO_S16LSB;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -111,10 +107,10 @@ bool CDROM_Interface_Image::BinaryFile::seek(Bit32u offset)
|
|||
return !file->fail();
|
||||
}
|
||||
|
||||
Bit16u CDROM_Interface_Image::BinaryFile::decode(Bit8u *buffer)
|
||||
Bit32u CDROM_Interface_Image::BinaryFile::decode(Bit16s *buffer, Bit32u desired_track_frames)
|
||||
{
|
||||
file->read((char*)buffer, chunkSize);
|
||||
return (Bit16u) file->gcount();
|
||||
file->read((char*)buffer, desired_track_frames * BYTES_PER_TRACK_FRAME);
|
||||
return (Bit32u) file->gcount() / BYTES_PER_TRACK_FRAME;
|
||||
}
|
||||
|
||||
CDROM_Interface_Image::AudioFile::AudioFile(const char *filename, bool &error)
|
||||
|
@ -122,7 +118,7 @@ CDROM_Interface_Image::AudioFile::AudioFile(const char *filename, bool &error)
|
|||
{
|
||||
// Use the audio file's actual sample rate and number of channels as opposed to overriding
|
||||
Sound_AudioInfo desired = {AUDIO_S16, 0, 0};
|
||||
sample = Sound_NewSampleFromFile(filename, &desired, chunkSize);
|
||||
sample = Sound_NewSampleFromFile(filename, &desired);
|
||||
if (sample) {
|
||||
error = false;
|
||||
std::string filename_only(filename);
|
||||
|
@ -166,11 +162,9 @@ bool CDROM_Interface_Image::AudioFile::seek(Bit32u offset)
|
|||
return result;
|
||||
}
|
||||
|
||||
Bit16u CDROM_Interface_Image::AudioFile::decode(Bit8u *buffer)
|
||||
Bit32u CDROM_Interface_Image::AudioFile::decode(Bit16s *buffer, Bit32u desired_track_frames)
|
||||
{
|
||||
const Bit16u bytes = Sound_Decode(sample);
|
||||
memcpy(buffer, sample->buffer, bytes);
|
||||
return bytes;
|
||||
return Sound_Decode_Direct(sample, (void*)buffer, desired_track_frames);
|
||||
}
|
||||
|
||||
Bit16u CDROM_Interface_Image::AudioFile::getEndian()
|
||||
|
@ -213,27 +207,21 @@ int CDROM_Interface_Image::AudioFile::getLength()
|
|||
int CDROM_Interface_Image::refCount = 0;
|
||||
CDROM_Interface_Image* CDROM_Interface_Image::images[26] = {};
|
||||
CDROM_Interface_Image::imagePlayer CDROM_Interface_Image::player = {
|
||||
{0}, // buffer[]
|
||||
{0}, // ctrlData struct
|
||||
nullptr, // TrackFile*
|
||||
nullptr, // MixerChannel*
|
||||
nullptr, // CDROM_Interface_Image*
|
||||
nullptr, // addSamples
|
||||
0, // startFrame
|
||||
0, // currFrame
|
||||
0, // numFrames
|
||||
0, // playbackTotal
|
||||
0, // playbackRemaining
|
||||
0, // bufferPos
|
||||
0, // bufferConsumed
|
||||
false, // isPlaying
|
||||
false, // isPaused
|
||||
false // ctrlUsed
|
||||
{0}, // buffer[]
|
||||
nullptr, // TrackFile*
|
||||
nullptr, // MixerChannel*
|
||||
nullptr, // CDROM_Interface_Image*
|
||||
nullptr, // addFrames
|
||||
0, // startRedbookFrame
|
||||
0, // totalRedbookFrames
|
||||
0, // playedTrackFrames
|
||||
0, // totalTrackFrames
|
||||
false, // isPlaying
|
||||
false // isPaused
|
||||
};
|
||||
|
||||
|
||||
|
||||
CDROM_Interface_Image::CDROM_Interface_Image(Bit8u subUnit)
|
||||
:subUnit(subUnit)
|
||||
:subUnit(subUnit)
|
||||
{
|
||||
images[subUnit] = this;
|
||||
if (refCount == 0) {
|
||||
|
@ -242,8 +230,7 @@ CDROM_Interface_Image::CDROM_Interface_Image(Bit8u subUnit)
|
|||
player.channel = MIXER_AddChannel(&CDAudioCallBack, 0, "CDAUDIO");
|
||||
player.channel->Enable(false);
|
||||
#ifdef DEBUG
|
||||
LOG_MSG("CDROM: Initialized with %d-byte circular buffer",
|
||||
AUDIO_DECODE_BUFFER_SIZE);
|
||||
LOG_MSG("%s CDROM: Initialized the CDAUDIO mixer channel", get_time());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -262,7 +249,7 @@ CDROM_Interface_Image::~CDROM_Interface_Image()
|
|||
MIXER_DelChannel(player.channel);
|
||||
player.channel = nullptr;
|
||||
#ifdef DEBUG
|
||||
LOG_MSG("CDROM: Audio channel freed");
|
||||
LOG_MSG("%s CDROM: Audio channel freed", get_time());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -343,38 +330,50 @@ bool CDROM_Interface_Image::GetAudioTrackInfo(int track, TMSF& start, unsigned c
|
|||
bool CDROM_Interface_Image::GetAudioSub(unsigned char& attr, unsigned char& track,
|
||||
unsigned char& index, TMSF& relPos, TMSF& absPos)
|
||||
{
|
||||
int cur_track = GetTrack(player.currFrame);
|
||||
if (cur_track < 1) {
|
||||
return false;
|
||||
}
|
||||
track = (unsigned char)cur_track;
|
||||
attr = tracks[track - 1].attr;
|
||||
index = 1;
|
||||
frames_to_msf(player.currFrame + 150, &absPos.min, &absPos.sec, &absPos.fr);
|
||||
frames_to_msf(player.currFrame - tracks[track - 1].start, &relPos.min, &relPos.sec, &relPos.fr);
|
||||
bool rcode = false;
|
||||
|
||||
// Convert our running tally of track-frames played to Redbook-frames played.
|
||||
// We round up because if our track-frame tally lands in the middle of a (fractional)
|
||||
// Redbook frame, then that Redbook frame would have to be considered played to produce
|
||||
// even the smallest amount of track-frames played. This also accurately represents
|
||||
// the very end of a sequence where the last Redbook frame might only contain a couple
|
||||
// PCM samples - but the entire last 2352-byte Redbook frame is needed to cover those samples.
|
||||
const Bit32u playedRedbookFrames = static_cast<Bit32u>(ceil(
|
||||
static_cast<float>(REDBOOK_FRAMES_PER_SECOND * player.playedTrackFrames) / player.trackFile->getRate() ));
|
||||
|
||||
// Add that to the track's starting Redbook frame to determine our absolute current Redbook frame
|
||||
const Bit32u currentRedbookFrame = player.startRedbookFrame + playedRedbookFrames;
|
||||
|
||||
const char cur_track = GetTrack(currentRedbookFrame);
|
||||
if (cur_track >= 1) {
|
||||
track = static_cast<unsigned char>(cur_track);
|
||||
attr = tracks[track - 1].attr;
|
||||
index = 1;
|
||||
frames_to_msf(currentRedbookFrame + 150, &absPos.min, &absPos.sec, &absPos.fr);
|
||||
frames_to_msf(currentRedbookFrame - tracks[track - 1].start, &relPos.min, &relPos.sec, &relPos.fr);
|
||||
rcode = true;
|
||||
|
||||
#ifdef DEBUG
|
||||
LOG_MSG("%s CDROM: GetAudioSub attr=%u, track=%u, index=%u",
|
||||
get_time(),
|
||||
attr,
|
||||
track,
|
||||
index);
|
||||
|
||||
LOG_MSG("%s CDROM: GetAudioSub absoute offset (%d), MSF=%d:%d:%d",
|
||||
get_time(),
|
||||
player.currFrame + 150,
|
||||
absPos.min,
|
||||
absPos.sec,
|
||||
absPos.fr);
|
||||
|
||||
LOG_MSG("%s CDROM: GetAudioSub relative offset (%d), MSF=%d:%d:%d",
|
||||
get_time(),
|
||||
player.currFrame - tracks[track - 1].start + 150,
|
||||
relPos.min,
|
||||
relPos.sec,
|
||||
relPos.fr);
|
||||
LOG_MSG("%s CDROM: GetAudioSub attr=%u, track=%u, index=%u",
|
||||
get_time(),
|
||||
attr,
|
||||
track,
|
||||
index);
|
||||
LOG_MSG("%s CDROM: GetAudioSub absoute offset (%d), MSF=%d:%d:%d",
|
||||
get_time(),
|
||||
currentRedbookFrame + 150,
|
||||
absPos.min,
|
||||
absPos.sec,
|
||||
absPos.fr);
|
||||
LOG_MSG("%s CDROM: GetAudioSub relative offset (%d), MSF=%d:%d:%d",
|
||||
get_time(),
|
||||
currentRedbookFrame - tracks[track - 1].start + 150,
|
||||
relPos.min,
|
||||
relPos.sec,
|
||||
relPos.fr);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return rcode;
|
||||
}
|
||||
|
||||
bool CDROM_Interface_Image::GetAudioStatus(bool& playing, bool& pause)
|
||||
|
@ -431,7 +430,7 @@ bool CDROM_Interface_Image::PlayAudioSector(unsigned long start, unsigned long l
|
|||
|
||||
// We can't play audio from a data track (as it would result in garbage/static)
|
||||
else if (track >= 0 && tracks[track].attr == 0x40)
|
||||
LOG(LOG_MISC,LOG_WARN)("Game tries to play the data track. Not doing this");
|
||||
LOG(LOG_MISC,LOG_WARN)("Game tried to play the data track. Not doing this");
|
||||
|
||||
// Checks passed, setup the audio stream
|
||||
else {
|
||||
|
@ -443,55 +442,46 @@ bool CDROM_Interface_Image::PlayAudioSector(unsigned long start, unsigned long l
|
|||
|
||||
// only initialize the player elements if our track is playable
|
||||
if (is_playable) {
|
||||
const Bit8u channels = trackFile->getChannels();
|
||||
const Bit32u rate = trackFile->getRate();
|
||||
const Bit8u track_channels = trackFile->getChannels();
|
||||
const Bit32u track_rate = trackFile->getRate();
|
||||
|
||||
player.cd = this;
|
||||
player.trackFile = trackFile;
|
||||
player.startFrame = start;
|
||||
player.currFrame = start;
|
||||
player.numFrames = len;
|
||||
player.bufferPos = 0;
|
||||
player.bufferConsumed = 0;
|
||||
player.startRedbookFrame = start;
|
||||
player.totalRedbookFrames = len;
|
||||
player.isPlaying = true;
|
||||
player.isPaused = false;
|
||||
|
||||
|
||||
#if defined(WORDS_BIGENDIAN)
|
||||
if (trackFile->getEndian() != AUDIO_S16SYS)
|
||||
#else
|
||||
if (trackFile->getEndian() == AUDIO_S16SYS)
|
||||
#endif
|
||||
player.addSamples = channels == 2 ? &MixerChannel::AddSamples_s16 \
|
||||
: &MixerChannel::AddSamples_m16;
|
||||
else
|
||||
player.addSamples = channels == 2 ? &MixerChannel::AddSamples_s16_nonnative \
|
||||
: &MixerChannel::AddSamples_m16_nonnative;
|
||||
|
||||
const float bytesPerMs = (float) (rate * channels * 2 / 1000.0);
|
||||
player.playbackTotal = lround(len * tracks[track].sectorSize * bytesPerMs / 176.4);
|
||||
player.playbackRemaining = player.playbackTotal;
|
||||
if (trackFile->getEndian() == AUDIO_S16SYS) {
|
||||
player.addFrames = track_channels == 2 ? &MixerChannel::AddSamples_s16 \
|
||||
: &MixerChannel::AddSamples_m16;
|
||||
} else {
|
||||
player.addFrames = track_channels == 2 ? &MixerChannel::AddSamples_s16_nonnative \
|
||||
: &MixerChannel::AddSamples_m16_nonnative;
|
||||
}
|
||||
// Convert Redbook frames to Track frames, rounding up to whole integer frames.
|
||||
// Round up to whole track frames because the content originated from whole redbook frames,
|
||||
// which will require the last fractional frames to be represented by a whole PCM frame.
|
||||
player.totalTrackFrames = static_cast<Bit32u>(ceil(
|
||||
static_cast<float>(track_rate * player.totalRedbookFrames) / REDBOOK_FRAMES_PER_SECOND));
|
||||
player.playedTrackFrames = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
LOG_MSG("%s CDROM: Playing track %d at %.1f KHz "
|
||||
"%d-channel at start sector %lu (%.1f minute-mark), seek %u "
|
||||
"(skip=%d,dstart=%d,secsize=%d), for %lu sectors (%.1f seconds)",
|
||||
LOG_MSG("%s CDROM: Playing track %d (%d Hz "
|
||||
"%d-channel) at starting sector %lu (%.1f minute-mark) "
|
||||
"for %u Redbook frames (%.1f seconds)",
|
||||
get_time(),
|
||||
track,
|
||||
rate/1000.0,
|
||||
channels,
|
||||
track_rate,
|
||||
track_channels,
|
||||
start,
|
||||
offset * (1/10584000.0),
|
||||
offset,
|
||||
tracks[track].skip,
|
||||
tracks[track].start,
|
||||
tracks[track].sectorSize,
|
||||
len,
|
||||
player.playbackRemaining / (1000 * bytesPerMs));
|
||||
static_cast<float>(start) / (REDBOOK_FRAMES_PER_SECOND * 60),
|
||||
player.totalRedbookFrames,
|
||||
static_cast<float>(player.totalRedbookFrames) / REDBOOK_FRAMES_PER_SECOND);
|
||||
#endif
|
||||
|
||||
// start the channel!
|
||||
player.channel->SetFreq(rate);
|
||||
player.channel->SetFreq(track_rate);
|
||||
player.channel->Enable(true);
|
||||
}
|
||||
}
|
||||
|
@ -533,8 +523,15 @@ bool CDROM_Interface_Image::StopAudio(void)
|
|||
|
||||
void CDROM_Interface_Image::ChannelControl(TCtrl ctrl)
|
||||
{
|
||||
player.ctrlUsed = (ctrl.out[0]!=0 || ctrl.out[1]!=1 || ctrl.vol[0]<0xfe || ctrl.vol[1]<0xfe);
|
||||
player.ctrlData = ctrl;
|
||||
if (player.channel == NULL) return;
|
||||
|
||||
// Adjust the volume of our mixer channel as defined by the application
|
||||
player.channel->SetScale(static_cast<float>(ctrl.vol[0]/255.0), // left vol
|
||||
static_cast<float>(ctrl.vol[1]/255.0)); // right vol
|
||||
|
||||
// Map the audio channels in our mixer channel as defined by the application
|
||||
player.channel->MapChannels(ctrl.out[0], // left map
|
||||
ctrl.out[1]); // right map
|
||||
}
|
||||
|
||||
bool CDROM_Interface_Image::ReadSectors(PhysPt buffer, bool raw, unsigned long sector, unsigned long num)
|
||||
|
@ -590,137 +587,47 @@ bool CDROM_Interface_Image::ReadSector(Bit8u *buffer, bool raw, unsigned long se
|
|||
if (tracks[track].sectorSize == RAW_SECTOR_SIZE && !tracks[track].mode2 && !raw) seek += 16;
|
||||
if (tracks[track].mode2 && !raw) seek += 24;
|
||||
|
||||
#if 0 // Excessively verbose.. only enable if needed
|
||||
#ifdef DEBUG
|
||||
LOG_MSG("CDROM: ReadSector track=%d, desired raw=%s, sector=%ld, length=%d",
|
||||
LOG_MSG("%s CDROM: ReadSector track=%d, desired raw=%s, sector=%ld, length=%d",
|
||||
get_time(),
|
||||
track,
|
||||
raw ? "true":"false",
|
||||
sector,
|
||||
length);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return tracks[track].file->read(buffer, seek, length);
|
||||
}
|
||||
|
||||
void CDROM_Interface_Image::CDAudioCallBack(Bitu len)
|
||||
|
||||
void CDROM_Interface_Image::CDAudioCallBack(Bitu desired_track_frames)
|
||||
{
|
||||
// Our member object "playbackRemaining" holds the
|
||||
// exact number of stream-bytes we need to play before meeting the
|
||||
// DOS program's desired playback duration in sectors. We simply
|
||||
// decrement this counter each callback until we're done.
|
||||
if (len == 0 || !player.isPlaying || player.isPaused) {
|
||||
return;
|
||||
}
|
||||
if (desired_track_frames > 0) {
|
||||
|
||||
// determine bytes per request (16-bit samples)
|
||||
const Bit8u channels = player.trackFile->getChannels();
|
||||
const Bit8u bytes_per_request = channels * 2;
|
||||
int total_requested = len * bytes_per_request;
|
||||
const Bit32u decoded_track_frames = player.trackFile->decode(player.buffer, desired_track_frames);
|
||||
|
||||
while (total_requested > 0) {
|
||||
int requested = total_requested;
|
||||
// uses either the stereo or mono and native or nonnative AddSamples call assigned during construction
|
||||
(player.channel->*player.addFrames)(decoded_track_frames, player.buffer);
|
||||
|
||||
// Every now and then the callback wants a big number of bytes,
|
||||
// which can exceed our circular buffer. In these cases we need
|
||||
// read through as many iteration of our circular buffer as needed.
|
||||
if (total_requested > AUDIO_DECODE_BUFFER_SIZE) {
|
||||
requested = AUDIO_DECODE_BUFFER_SIZE;
|
||||
total_requested -= AUDIO_DECODE_BUFFER_SIZE;
|
||||
}
|
||||
else {
|
||||
total_requested = 0;
|
||||
// Stop the audio if our decode stream has run dry or when we've played at least the
|
||||
// total number of frames. We consider "played" to mean the running tally so far plus
|
||||
// the current requested frames, which takes into account that the number of currently
|
||||
// decoded frames might be less than desired if we're at the end of the track.
|
||||
// (The mixer will request frames forever until we stop it, so at some point we /will/
|
||||
// decode fewer than requested; in this "tail scenario", we push those remaining decoded
|
||||
// frames into the mixer and then stop the audio.)
|
||||
if (decoded_track_frames == 0
|
||||
|| player.playedTrackFrames + desired_track_frames >= player.totalTrackFrames) {
|
||||
player.cd->StopAudio();
|
||||
}
|
||||
|
||||
// Three scenarios in order of probabilty:
|
||||
//
|
||||
// 1. Consume: If our decoded circular buffer is sufficiently filled to
|
||||
// satify the Mixer's requested size, then feed the Mixer with
|
||||
// the requested number of bytes.
|
||||
//
|
||||
// 2. Wrap: If we've decoded and consumed to edge of our buffer, then
|
||||
// we need to wrap any remaining decoded-but-not-consumed
|
||||
// samples back around to the front of the buffer.
|
||||
//
|
||||
// 3. Fill: When our circular buffer is too depleted to satisfy the
|
||||
// Mixer's requested size, then ask the decoder for more data;
|
||||
// to either enough to completely fill our buffer or whatever
|
||||
// is left to satisfy the remaining samples in this requested
|
||||
// playback sequence (the Mixer is unaware of how much audio
|
||||
// xwe need to play; instead we decide when to cutoff the mixer).
|
||||
//
|
||||
while (true) {
|
||||
|
||||
// 1. Consume
|
||||
if (player.bufferPos - player.bufferConsumed >= requested) {
|
||||
if (player.ctrlUsed) {
|
||||
for (Bit8u i=0; i < channels; i++) {
|
||||
Bit16s sample;
|
||||
Bit16s* samples = (Bit16s*)&player.buffer[player.bufferConsumed];
|
||||
for (int pos = 0; pos < requested / bytes_per_request; pos++) {
|
||||
#if defined(WORDS_BIGENDIAN)
|
||||
sample = (Bit16s)host_readw((HostPt) & samples[pos * 2 + player.ctrlData.out[i]]);
|
||||
#else
|
||||
sample = samples[pos * 2 + player.ctrlData.out[i]];
|
||||
#endif
|
||||
samples[pos * 2 + i] = (Bit16s)(sample * player.ctrlData.vol[i] / 255.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// uses either the stereo or mono and native or nonnative AddSamples
|
||||
// call assigned during construction
|
||||
(player.channel->*player.addSamples)(requested / bytes_per_request,
|
||||
(Bit16s*)(player.buffer + player.bufferConsumed) );
|
||||
player.bufferConsumed += requested;
|
||||
player.playbackRemaining -= requested;
|
||||
|
||||
// Games can query the current Red Book MSF frame-position, so we keep that up-to-date here.
|
||||
// We scale the final number of frames by the percent complete, which
|
||||
// avoids having to keep track of the equivlent number of Red Book frames
|
||||
// read (which would involve coverting the compressed streams data-rate into
|
||||
// CDROM Red Book rate, which is more work than simply scaling).
|
||||
//
|
||||
const double playbackPercentSoFar = (player.playbackTotal - player.playbackRemaining) / player.playbackTotal;
|
||||
player.currFrame = player.startFrame + (Bit32u) ceil(player.numFrames * playbackPercentSoFar);
|
||||
break;
|
||||
}
|
||||
|
||||
// 2. Wrap
|
||||
else {
|
||||
memcpy(player.buffer,
|
||||
player.buffer + player.bufferConsumed,
|
||||
player.bufferPos - player.bufferConsumed);
|
||||
player.bufferPos -= player.bufferConsumed;
|
||||
player.bufferConsumed = 0;
|
||||
}
|
||||
|
||||
// 3. Fill
|
||||
const Bit16u chunkSize = player.trackFile->chunkSize;
|
||||
while(AUDIO_DECODE_BUFFER_SIZE - player.bufferPos >= chunkSize &&
|
||||
(player.bufferPos - player.bufferConsumed < player.playbackRemaining ||
|
||||
player.bufferPos - player.bufferConsumed < requested) ) {
|
||||
|
||||
const Bit16u decoded = player.trackFile->decode(player.buffer + player.bufferPos);
|
||||
player.bufferPos += decoded;
|
||||
|
||||
// if we decoded less than expected, which could be due to EOF or if the CUE file specified
|
||||
// an exact "INDEX 01 MIN:SEC:FRAMES" value but the compressed track is ever-so-slightly less than
|
||||
// that specified, then simply pad with zeros.
|
||||
const Bit16s underDecode = chunkSize - decoded;
|
||||
if (underDecode > 0) {
|
||||
|
||||
#ifdef DEBUG
|
||||
LOG_MSG("%s CDROM: Underdecoded by %d. Feeding mixer with zeros.",
|
||||
get_time(),
|
||||
underDecode);
|
||||
#endif
|
||||
|
||||
memset(player.buffer + player.bufferPos, 0, underDecode);
|
||||
player.bufferPos += underDecode;
|
||||
}
|
||||
} // end of fill-while
|
||||
} // end of decode and fill loop
|
||||
} // end while total_requested
|
||||
|
||||
if (player.playbackRemaining <= 0) {
|
||||
player.cd->StopAudio();
|
||||
// Increment our tally of played frames by those we just decoded (and fed to the mixer).
|
||||
// Even if we've hit the end of the track and we've stopped the audio, we still want to
|
||||
// increment our tally so subsequent calls to GetAudioSub() return the full number of
|
||||
// frames played.
|
||||
player.playedTrackFrames += decoded_track_frames;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -470,8 +470,15 @@ bool CDROM_Interface_Ioctl::StopAudio(void) {
|
|||
|
||||
void CDROM_Interface_Ioctl::ChannelControl(TCtrl ctrl)
|
||||
{
|
||||
player.ctrlUsed = (ctrl.out[0]!=0 || ctrl.out[1]!=1 || ctrl.vol[0]<0xfe || ctrl.vol[1]<0xfe);
|
||||
player.ctrlData = ctrl;
|
||||
if (player.channel == NULL) return;
|
||||
|
||||
// Adjust the volme of our mixer channel as defined by the application
|
||||
player.channel->SetScale(static_cast<float>(ctrl.vol[0]/255.0), // left vol
|
||||
static_cast<float>(ctrl.vol[1]/255.0)); // right vol
|
||||
|
||||
// Map the audio channels in our mixer channel as defined by the application
|
||||
player.channel->MapChannels(ctrl.out[0], // left map
|
||||
ctrl.out[1]); // right map
|
||||
}
|
||||
|
||||
bool CDROM_Interface_Ioctl::LoadUnloadMedia(bool unload) {
|
||||
|
@ -565,16 +572,6 @@ void CDROM_Interface_Ioctl::dx_CDAudioCallBack(Bitu len) {
|
|||
}
|
||||
}
|
||||
SDL_mutexV(player.mutex);
|
||||
if (player.ctrlUsed) {
|
||||
Bit16s sample0,sample1;
|
||||
Bit16s * samples=(Bit16s *)&player.buffer;
|
||||
for (Bitu pos=0;pos<len/4;pos++) {
|
||||
sample0=samples[pos*2+player.ctrlData.out[0]];
|
||||
sample1=samples[pos*2+player.ctrlData.out[1]];
|
||||
samples[pos*2+0]=(Bit16s)(sample0*player.ctrlData.vol[0]/255.0);
|
||||
samples[pos*2+1]=(Bit16s)(sample1*player.ctrlData.vol[1]/255.0);
|
||||
}
|
||||
}
|
||||
player.channel->AddSamples_s16(len/4,(Bit16s *)player.buffer);
|
||||
memmove(player.buffer, &player.buffer[len], player.bufLen - len);
|
||||
player.bufLen -= len;
|
||||
|
|
|
@ -22,15 +22,20 @@
|
|||
That should call the mixer start from there or something.
|
||||
*/
|
||||
|
||||
// #define DEBUG 1
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined (WIN32)
|
||||
//Midi listing
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
// prevent the Windows header from clobbering std::min and max
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#endif
|
||||
|
@ -90,16 +95,35 @@ static struct {
|
|||
|
||||
Bit8u MixTemp[MIXER_BUFSIZE];
|
||||
|
||||
MixerChannel * MIXER_AddChannel(MIXER_Handler handler,Bitu freq,const char * name) {
|
||||
MixerChannel * chan=new MixerChannel();
|
||||
chan->scale = 1.0;
|
||||
chan->handler=handler;
|
||||
chan->name=name;
|
||||
chan->SetFreq(freq);
|
||||
MixerChannel::MixerChannel(MIXER_Handler _handler, Bitu _freq, const char * _name) :
|
||||
// Public member initialization
|
||||
volmain {0, 0},
|
||||
next (nullptr),
|
||||
name (_name),
|
||||
done (0),
|
||||
enabled (false),
|
||||
|
||||
// Private member initialization
|
||||
handler (_handler),
|
||||
freq_add (0),
|
||||
freq_counter (0),
|
||||
needed (0),
|
||||
prev_sample {0, 0},
|
||||
next_sample {0, 0},
|
||||
volmul {0, 0},
|
||||
scale {0, 0},
|
||||
channel_map {0, 0},
|
||||
interpolate (false) {
|
||||
}
|
||||
|
||||
MixerChannel * MIXER_AddChannel(MIXER_Handler handler, Bitu freq, const char * name) {
|
||||
MixerChannel * chan=new MixerChannel(handler, freq, name);
|
||||
chan->next=mixer.channels;
|
||||
chan->SetVolume(1,1);
|
||||
chan->enabled=false;
|
||||
chan->interpolate = false;
|
||||
chan->SetFreq(freq); // also enables 'interpolate' if needed
|
||||
chan->SetScale(1.0);
|
||||
chan->SetVolume(1, 1);
|
||||
chan->MapChannels(0, 1);
|
||||
chan->Enable(false);
|
||||
mixer.channels=chan;
|
||||
return chan;
|
||||
}
|
||||
|
@ -128,19 +152,59 @@ void MIXER_DelChannel(MixerChannel* delchan) {
|
|||
}
|
||||
|
||||
void MixerChannel::UpdateVolume(void) {
|
||||
volmul[0]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[0]*mixer.mastervol[0]);
|
||||
volmul[1]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[1]*mixer.mastervol[1]);
|
||||
volmul[0]=(Bits)((1 << MIXER_VOLSHIFT)*scale[0]*volmain[0]*mixer.mastervol[0]);
|
||||
volmul[1]=(Bits)((1 << MIXER_VOLSHIFT)*scale[1]*volmain[1]*mixer.mastervol[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T clamp(const T& n, const T& lower, const T& upper) {
|
||||
return std::max(lower, std::min(n, upper));
|
||||
}
|
||||
|
||||
void MixerChannel::SetVolume(float _left,float _right) {
|
||||
volmain[0]=_left;
|
||||
volmain[1]=_right;
|
||||
// Allow unconstrained user-defined values
|
||||
volmain[0] = _left;
|
||||
volmain[1] = _right;
|
||||
UpdateVolume();
|
||||
}
|
||||
|
||||
void MixerChannel::SetScale( float f ) {
|
||||
scale = f;
|
||||
UpdateVolume();
|
||||
void MixerChannel::SetScale(float f) {
|
||||
SetScale(f, f);
|
||||
}
|
||||
|
||||
void MixerChannel::SetScale(float _left, float _right) {
|
||||
// Constrain application-defined volume between 0% and 100%
|
||||
const float min_volume(0.0);
|
||||
const float max_volume(1.0);
|
||||
_left = clamp(_left, min_volume, max_volume);
|
||||
_right = clamp(_right, min_volume, max_volume);
|
||||
if (scale[0] != _left || scale[1] != _right) {
|
||||
scale[0] = _left;
|
||||
scale[1] = _right;
|
||||
UpdateVolume();
|
||||
#ifdef DEBUG
|
||||
LOG_MSG("MIXER %-7s channel: application changed left and right volumes to %3.0f%% and %3.0f%%, respectively",
|
||||
name, scale[0] * 100, scale[1] * 100);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MixerChannel::MapChannels(Bit8u _left, Bit8u _right) {
|
||||
// Constrain mapping to the 0 (left) and 1 (right) channel indexes
|
||||
const Bit8u min_channel(0);
|
||||
const Bit8u max_channel(1);
|
||||
_left = clamp(_left, min_channel, max_channel);
|
||||
_right = clamp(_right, min_channel, max_channel);
|
||||
if (channel_map[0] != _left || channel_map[1] != _right) {
|
||||
channel_map[0] = _left;
|
||||
channel_map[1] = _right;
|
||||
#ifdef DEBUG
|
||||
LOG_MSG("MIXER %-7s channel: application changed audio-channel mapping to left=>%s and right=>%s",
|
||||
name,
|
||||
channel_map[0] == 0 ? "left" : "right",
|
||||
channel_map[1] == 0 ? "left" : "right");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MixerChannel::Enable(bool _yesno) {
|
||||
|
@ -179,8 +243,8 @@ void MixerChannel::AddSilence(void) {
|
|||
if (done<needed) {
|
||||
done=needed;
|
||||
//Make sure the next samples are zero when they get switched to prev
|
||||
nextSample[0] = 0;
|
||||
nextSample[1] = 0;
|
||||
next_sample[0] = 0;
|
||||
next_sample[1] = 0;
|
||||
//This should trigger an instant request for new samples
|
||||
freq_counter = FREQ_NEXT;
|
||||
}
|
||||
|
@ -200,24 +264,26 @@ inline void MixerChannel::AddSamples(Bitu len, const Type* data) {
|
|||
if (pos >= len)
|
||||
return;
|
||||
freq_counter -= FREQ_NEXT;
|
||||
prevSample[0] = nextSample[0];
|
||||
|
||||
prev_sample[0] = next_sample[0];
|
||||
if (stereo) {
|
||||
prevSample[1] = nextSample[1];
|
||||
prev_sample[1] = next_sample[1];
|
||||
}
|
||||
|
||||
if ( sizeof( Type) == 1) {
|
||||
if (!signeddata) {
|
||||
if (stereo) {
|
||||
nextSample[0]=(((Bit8s)(data[pos*2+0] ^ 0x80)) << 8);
|
||||
nextSample[1]=(((Bit8s)(data[pos*2+1] ^ 0x80)) << 8);
|
||||
next_sample[0]=(((Bit8s)(data[pos*2+0] ^ 0x80)) << 8);
|
||||
next_sample[1]=(((Bit8s)(data[pos*2+1] ^ 0x80)) << 8);
|
||||
} else {
|
||||
nextSample[0]=(((Bit8s)(data[pos] ^ 0x80)) << 8);
|
||||
next_sample[0]=(((Bit8s)(data[pos] ^ 0x80)) << 8);
|
||||
}
|
||||
} else {
|
||||
if (stereo) {
|
||||
nextSample[0]=(data[pos*2+0] << 8);
|
||||
nextSample[1]=(data[pos*2+1] << 8);
|
||||
next_sample[0]=(data[pos*2+0] << 8);
|
||||
next_sample[1]=(data[pos*2+1] << 8);
|
||||
} else {
|
||||
nextSample[0]=(data[pos] << 8);
|
||||
next_sample[0]=(data[pos] << 8);
|
||||
}
|
||||
}
|
||||
//16bit and 32bit both contain 16bit data internally
|
||||
|
@ -225,50 +291,50 @@ inline void MixerChannel::AddSamples(Bitu len, const Type* data) {
|
|||
if (signeddata) {
|
||||
if (stereo) {
|
||||
if (nativeorder) {
|
||||
nextSample[0]=data[pos*2+0];
|
||||
nextSample[1]=data[pos*2+1];
|
||||
next_sample[0]=data[pos*2+0];
|
||||
next_sample[1]=data[pos*2+1];
|
||||
} else {
|
||||
if ( sizeof( Type) == 2) {
|
||||
nextSample[0]=(Bit16s)host_readw((HostPt)&data[pos*2+0]);
|
||||
nextSample[1]=(Bit16s)host_readw((HostPt)&data[pos*2+1]);
|
||||
next_sample[0]=(Bit16s)host_readw((HostPt)&data[pos*2+0]);
|
||||
next_sample[1]=(Bit16s)host_readw((HostPt)&data[pos*2+1]);
|
||||
} else {
|
||||
nextSample[0]=(Bit32s)host_readd((HostPt)&data[pos*2+0]);
|
||||
nextSample[1]=(Bit32s)host_readd((HostPt)&data[pos*2+1]);
|
||||
next_sample[0]=(Bit32s)host_readd((HostPt)&data[pos*2+0]);
|
||||
next_sample[1]=(Bit32s)host_readd((HostPt)&data[pos*2+1]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (nativeorder) {
|
||||
nextSample[0] = data[pos];
|
||||
next_sample[0] = data[pos];
|
||||
} else {
|
||||
if ( sizeof( Type) == 2) {
|
||||
nextSample[0]=(Bit16s)host_readw((HostPt)&data[pos]);
|
||||
next_sample[0]=(Bit16s)host_readw((HostPt)&data[pos]);
|
||||
} else {
|
||||
nextSample[0]=(Bit32s)host_readd((HostPt)&data[pos]);
|
||||
next_sample[0]=(Bit32s)host_readd((HostPt)&data[pos]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (stereo) {
|
||||
if (nativeorder) {
|
||||
nextSample[0]=(Bits)data[pos*2+0]-32768;
|
||||
nextSample[1]=(Bits)data[pos*2+1]-32768;
|
||||
next_sample[0]=(Bits)data[pos*2+0]-32768;
|
||||
next_sample[1]=(Bits)data[pos*2+1]-32768;
|
||||
} else {
|
||||
if ( sizeof( Type) == 2) {
|
||||
nextSample[0]=(Bits)host_readw((HostPt)&data[pos*2+0])-32768;
|
||||
nextSample[1]=(Bits)host_readw((HostPt)&data[pos*2+1])-32768;
|
||||
next_sample[0]=(Bits)host_readw((HostPt)&data[pos*2+0])-32768;
|
||||
next_sample[1]=(Bits)host_readw((HostPt)&data[pos*2+1])-32768;
|
||||
} else {
|
||||
nextSample[0]=(Bits)host_readd((HostPt)&data[pos*2+0])-32768;
|
||||
nextSample[1]=(Bits)host_readd((HostPt)&data[pos*2+1])-32768;
|
||||
next_sample[0]=(Bits)host_readd((HostPt)&data[pos*2+0])-32768;
|
||||
next_sample[1]=(Bits)host_readd((HostPt)&data[pos*2+1])-32768;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (nativeorder) {
|
||||
nextSample[0]=(Bits)data[pos]-32768;
|
||||
next_sample[0]=(Bits)data[pos]-32768;
|
||||
} else {
|
||||
if ( sizeof( Type) == 2) {
|
||||
nextSample[0]=(Bits)host_readw((HostPt)&data[pos])-32768;
|
||||
next_sample[0]=(Bits)host_readw((HostPt)&data[pos])-32768;
|
||||
} else {
|
||||
nextSample[0]=(Bits)host_readd((HostPt)&data[pos])-32768;
|
||||
next_sample[0]=(Bits)host_readd((HostPt)&data[pos])-32768;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -277,19 +343,28 @@ inline void MixerChannel::AddSamples(Bitu len, const Type* data) {
|
|||
//This sample has been handled now, increase position
|
||||
pos++;
|
||||
}
|
||||
|
||||
//Apply the left and right channel mappers only on write[..]
|
||||
//assignments. This ensures the channels are mapped only once
|
||||
//(avoiding double-swapping) and also minimizes the places where
|
||||
//we use our mapping variables as array indexes.
|
||||
//Note that volumes are independent of the channels mapping.
|
||||
const Bit8u left_map(channel_map[0]);
|
||||
const Bit8u right_map(channel_map[1]);
|
||||
|
||||
//Where to write
|
||||
mixpos &= MIXER_BUFMASK;
|
||||
Bit32s* write = mixer.work[mixpos];
|
||||
if (!interpolate) {
|
||||
write[0] += prevSample[0] * volmul[0];
|
||||
write[1] += (stereo ? prevSample[1] : prevSample[0]) * volmul[1];
|
||||
write[0] += prev_sample[left_map] * volmul[0];
|
||||
write[1] += (stereo ? prev_sample[right_map] : prev_sample[left_map]) * volmul[1];
|
||||
}
|
||||
else {
|
||||
Bits diff_mul = freq_counter & FREQ_MASK;
|
||||
Bits sample = prevSample[0] + (((nextSample[0] - prevSample[0]) * diff_mul) >> FREQ_SHIFT);
|
||||
Bits sample = prev_sample[left_map] + (((next_sample[left_map] - prev_sample[left_map]) * diff_mul) >> FREQ_SHIFT);
|
||||
write[0] += sample*volmul[0];
|
||||
if (stereo) {
|
||||
sample = prevSample[1] + (((nextSample[1] - prevSample[1]) * diff_mul) >> FREQ_SHIFT);
|
||||
sample = prev_sample[right_map] + (((next_sample[right_map] - prev_sample[right_map]) * diff_mul) >> FREQ_SHIFT);
|
||||
}
|
||||
write[1] += sample*volmul[1];
|
||||
}
|
||||
|
@ -318,14 +393,14 @@ void MixerChannel::AddStretched(Bitu len,Bit16s * data) {
|
|||
if (pos != new_pos) {
|
||||
pos = new_pos;
|
||||
//Forward the previous sample
|
||||
prevSample[0] = data[0];
|
||||
prev_sample[0] = data[0];
|
||||
data++;
|
||||
}
|
||||
Bits diff = data[0] - prevSample[0];
|
||||
Bits diff = data[0] - prev_sample[0];
|
||||
Bits diff_mul = index & FREQ_MASK;
|
||||
index += index_add;
|
||||
mixpos &= MIXER_BUFMASK;
|
||||
Bits sample = prevSample[0] + ((diff * diff_mul) >> FREQ_SHIFT);
|
||||
Bits sample = prev_sample[0] + ((diff * diff_mul) >> FREQ_SHIFT);
|
||||
mixer.work[mixpos][0] += sample * volmul[0];
|
||||
mixer.work[mixpos][1] += sample * volmul[1];
|
||||
mixpos++;
|
||||
|
@ -702,7 +777,7 @@ void MIXER_Init(Section* sec) {
|
|||
mixer.tick_add=calc_tickadd(mixer.freq);
|
||||
TIMER_AddTickHandler(MIXER_Mix_NoSound);
|
||||
} else {
|
||||
if((mixer.freq != obtained.freq) || (mixer.blocksize != obtained.samples))
|
||||
if((static_cast<Bit16s>(mixer.freq) != obtained.freq) || (mixer.blocksize != obtained.samples))
|
||||
LOG_MSG("MIXER: Got different values from SDL: freq %d, blocksize %d",obtained.freq,obtained.samples);
|
||||
mixer.freq=obtained.freq;
|
||||
mixer.blocksize=obtained.samples;
|
||||
|
|
Loading…
Add table
Reference in a new issue