Add Csaba and Attila's Gameboy instrument
git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@1584 0778d3d1-df1d-0410-868b-ea421aaaa00d
@@ -10,6 +10,7 @@ ADD_SUBDIRECTORY(lb302)
|
||||
ADD_SUBDIRECTORY(live_tool)
|
||||
ADD_SUBDIRECTORY(midi_import)
|
||||
ADD_SUBDIRECTORY(organic)
|
||||
ADD_SUBDIRECTORY(papu)
|
||||
ADD_SUBDIRECTORY(patman)
|
||||
ADD_SUBDIRECTORY(peak_controller_effect)
|
||||
ADD_SUBDIRECTORY(sf2_player)
|
||||
|
||||
83
plugins/papu/Basic_Gb_Apu.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/libs/
|
||||
|
||||
#include "Basic_Gb_Apu.h"
|
||||
|
||||
/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details. You should have received a copy of the GNU Lesser General
|
||||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
gb_time_t const frame_length = 70224;
|
||||
|
||||
Basic_Gb_Apu::Basic_Gb_Apu()
|
||||
{
|
||||
time = 0;
|
||||
|
||||
// Adjust frequency equalization to make it sound like a tiny speaker
|
||||
apu.treble_eq( -20.0 ); // lower values muffle it more
|
||||
buf.bass_freq( 461 ); // higher values simulate smaller speaker
|
||||
}
|
||||
|
||||
Basic_Gb_Apu::~Basic_Gb_Apu()
|
||||
{
|
||||
}
|
||||
|
||||
blargg_err_t Basic_Gb_Apu::set_sample_rate( long rate )
|
||||
{
|
||||
apu.output( buf.center(), buf.left(), buf.right() );
|
||||
buf.clock_rate( 4194304 );
|
||||
return buf.set_sample_rate( rate );
|
||||
}
|
||||
|
||||
void Basic_Gb_Apu::write_register( gb_addr_t addr, int data )
|
||||
{
|
||||
apu.write_register( clock(), addr, data );
|
||||
}
|
||||
|
||||
int Basic_Gb_Apu::read_register( gb_addr_t addr )
|
||||
{
|
||||
return apu.read_register( clock(), addr );
|
||||
}
|
||||
|
||||
void Basic_Gb_Apu::end_frame()
|
||||
{
|
||||
time = 0;
|
||||
bool stereo = apu.end_frame( frame_length );
|
||||
buf.end_frame( frame_length, stereo );
|
||||
}
|
||||
|
||||
long Basic_Gb_Apu::samples_avail() const
|
||||
{
|
||||
return buf.samples_avail();
|
||||
}
|
||||
|
||||
long Basic_Gb_Apu::read_samples( sample_t* out, long count )
|
||||
{
|
||||
return buf.read_samples( out, count );
|
||||
}
|
||||
|
||||
//added by 589 --->
|
||||
|
||||
void Basic_Gb_Apu::reset()
|
||||
{
|
||||
apu.reset();
|
||||
}
|
||||
|
||||
void Basic_Gb_Apu::treble_eq( const blip_eq_t& eq )
|
||||
{
|
||||
apu.treble_eq( eq );
|
||||
}
|
||||
|
||||
void Basic_Gb_Apu::bass_freq( int bf )
|
||||
{
|
||||
buf.bass_freq( bf );
|
||||
}
|
||||
|
||||
// <---
|
||||
53
plugins/papu/Basic_Gb_Apu.h
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
// Simplified Nintendo Game Boy PAPU sound chip emulator
|
||||
|
||||
// Gb_Snd_Emu 0.1.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
|
||||
|
||||
#ifndef BASIC_GB_APU_H
|
||||
#define BASIC_GB_APU_H
|
||||
|
||||
#include "gb_apu/Gb_Apu.h"
|
||||
#include "gb_apu/Multi_Buffer.h"
|
||||
|
||||
class Basic_Gb_Apu {
|
||||
public:
|
||||
Basic_Gb_Apu();
|
||||
~Basic_Gb_Apu();
|
||||
|
||||
// Set output sample rate
|
||||
blargg_err_t set_sample_rate( long rate );
|
||||
|
||||
// Pass reads and writes in the range 0xff10-0xff3f
|
||||
void write_register( gb_addr_t, int data );
|
||||
int read_register( gb_addr_t );
|
||||
|
||||
// End a 1/60 sound frame and add samples to buffer
|
||||
void end_frame();
|
||||
|
||||
// Samples are generated in stereo, left first. Sample counts are always
|
||||
// a multiple of 2.
|
||||
|
||||
// Number of samples in buffer
|
||||
long samples_avail() const;
|
||||
|
||||
// Read at most 'count' samples out of buffer and return number actually read
|
||||
typedef blip_sample_t sample_t;
|
||||
long read_samples( sample_t* out, long count );
|
||||
|
||||
//added by 589 --->
|
||||
void reset();
|
||||
void treble_eq( const blip_eq_t& eq );
|
||||
void bass_freq( int bf );
|
||||
//<---
|
||||
|
||||
private:
|
||||
Gb_Apu apu;
|
||||
Stereo_Buffer buf;
|
||||
blip_time_t time;
|
||||
|
||||
// faked CPU timing
|
||||
blip_time_t clock() { return time += 4; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
3
plugins/papu/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
INCLUDE(BuildPlugin)
|
||||
|
||||
BUILD_PLUGIN(papu papu_instrument.cpp papu_instrument.h Basic_Gb_Apu.cpp Basic_Gb_Apu.h gb_apu/Gb_Oscs.cpp gb_apu/Gb_Apu.h gb_apu/Blip_Buffer.cpp gb_apu/Gb_Apu.cpp gb_apu/Gb_Oscs.h gb_apu/blargg_common.h gb_apu/Blip_Buffer.h gb_apu/Multi_Buffer.cpp gb_apu/blargg_source.h gb_apu/Blip_Synth.h gb_apu/Multi_Buffer.h MOCFILES papu_instrument.h EMBEDDED_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.png)
|
||||
BIN
plugins/papu/artwork.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
plugins/papu/btn_15.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
plugins/papu/btn_7.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
plugins/papu/btn_down.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
plugins/papu/btn_off.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
plugins/papu/btn_on.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
plugins/papu/btn_up.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
429
plugins/papu/gb_apu/Blip_Buffer.cpp
Normal file
@@ -0,0 +1,429 @@
|
||||
|
||||
// Blip_Buffer 0.3.4. http://www.slack.net/~ant/libs/
|
||||
|
||||
#include "Blip_Buffer.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <math.h>
|
||||
|
||||
/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details. You should have received a copy of the GNU Lesser General
|
||||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include BLARGG_SOURCE_BEGIN
|
||||
|
||||
Blip_Buffer::Blip_Buffer()
|
||||
{
|
||||
samples_per_sec = 44100;
|
||||
buffer_ = NULL;
|
||||
|
||||
// try to cause assertion failure if buffer is used before these are set
|
||||
clocks_per_sec = 0;
|
||||
factor_ = ~0ul;
|
||||
offset_ = 0;
|
||||
buffer_size_ = 0;
|
||||
length_ = 0;
|
||||
|
||||
bass_freq_ = 16;
|
||||
}
|
||||
|
||||
void Blip_Buffer::clear( bool entire_buffer )
|
||||
{
|
||||
long count = (entire_buffer ? buffer_size_ : samples_avail());
|
||||
offset_ = 0;
|
||||
reader_accum = 0;
|
||||
if ( buffer_ )
|
||||
memset( buffer_, sample_offset_ & 0xFF, (count + widest_impulse_) * sizeof (buf_t_) );
|
||||
}
|
||||
|
||||
blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec )
|
||||
{
|
||||
unsigned new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) + 1 - widest_impulse_ - 64;
|
||||
if ( msec != blip_default_length )
|
||||
{
|
||||
size_t s = (new_rate * (msec + 1) + 999) / 1000;
|
||||
if ( s < new_size )
|
||||
new_size = s;
|
||||
else
|
||||
require( false ); // requested buffer length exceeds limit
|
||||
}
|
||||
|
||||
if ( buffer_size_ != new_size )
|
||||
{
|
||||
delete [] buffer_;
|
||||
buffer_ = NULL; // allow for exception in allocation below
|
||||
buffer_size_ = 0;
|
||||
offset_ = 0;
|
||||
|
||||
int const count_clocks_extra = 2;
|
||||
buffer_ = BLARGG_NEW buf_t_ [new_size + widest_impulse_ + count_clocks_extra];
|
||||
BLARGG_CHECK_ALLOC( buffer_ );
|
||||
}
|
||||
|
||||
buffer_size_ = new_size;
|
||||
length_ = new_size * 1000 / new_rate - 1;
|
||||
if ( msec )
|
||||
assert( length_ == msec ); // ensure length is same as that passed in
|
||||
|
||||
samples_per_sec = new_rate;
|
||||
if ( clocks_per_sec )
|
||||
clock_rate( clocks_per_sec ); // recalculate factor
|
||||
|
||||
bass_freq( bass_freq_ ); // recalculate shift
|
||||
|
||||
clear();
|
||||
|
||||
return blargg_success;
|
||||
}
|
||||
|
||||
blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const
|
||||
{
|
||||
blip_resampled_time_t factor = (unsigned long) floor(
|
||||
(double) samples_per_sec / clock_rate * (1L << BLIP_BUFFER_ACCURACY) + 0.5 );
|
||||
require( factor > 0 ); // clock_rate/sample_rate ratio is too large
|
||||
return factor;
|
||||
}
|
||||
|
||||
Blip_Buffer::~Blip_Buffer()
|
||||
{
|
||||
delete [] buffer_;
|
||||
}
|
||||
|
||||
void Blip_Buffer::bass_freq( int freq )
|
||||
{
|
||||
bass_freq_ = freq;
|
||||
if ( freq == 0 )
|
||||
{
|
||||
bass_shift = 31; // 32 or greater invokes undefined behavior elsewhere
|
||||
return;
|
||||
}
|
||||
bass_shift = 1 + (int) floor( 1.442695041 * log( 0.124 * samples_per_sec / freq ) );
|
||||
if ( bass_shift < 0 )
|
||||
bass_shift = 0;
|
||||
if ( bass_shift > 24 )
|
||||
bass_shift = 24;
|
||||
}
|
||||
|
||||
long Blip_Buffer::count_samples( blip_time_t t ) const
|
||||
{
|
||||
return (resampled_time( t ) >> BLIP_BUFFER_ACCURACY) - (offset_ >> BLIP_BUFFER_ACCURACY);
|
||||
}
|
||||
|
||||
blip_time_t Blip_Buffer::count_clocks( long count ) const
|
||||
{
|
||||
if ( count > buffer_size_ )
|
||||
count = buffer_size_;
|
||||
|
||||
return ((count << BLIP_BUFFER_ACCURACY) - offset_ + (factor_ - 1)) / factor_;
|
||||
}
|
||||
|
||||
void Blip_Impulse_::init( blip_pair_t_* imps, int w, int r, int fb )
|
||||
{
|
||||
fine_bits = fb;
|
||||
width = w;
|
||||
impulses = (imp_t*) imps;
|
||||
generate = true;
|
||||
volume_unit_ = -1.0;
|
||||
res = r;
|
||||
buf = NULL;
|
||||
|
||||
impulse = &impulses [width * res * 2 * (fine_bits ? 2 : 1)];
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
const int impulse_bits = 15;
|
||||
const long impulse_amp = 1L << impulse_bits;
|
||||
const long impulse_offset = impulse_amp / 2;
|
||||
|
||||
void Blip_Impulse_::scale_impulse( int unit, imp_t* imp_in ) const
|
||||
{
|
||||
long offset = ((long) unit << impulse_bits) - impulse_offset * unit +
|
||||
(1 << (impulse_bits - 1));
|
||||
imp_t* imp = imp_in;
|
||||
imp_t* fimp = impulse;
|
||||
for ( int n = res / 2 + 1; n--; )
|
||||
{
|
||||
int error = unit;
|
||||
for ( int nn = width; nn--; )
|
||||
{
|
||||
long a = ((long) *fimp++ * unit + offset) >> impulse_bits;
|
||||
error -= a - unit;
|
||||
*imp++ = (imp_t) a;
|
||||
}
|
||||
|
||||
// add error to middle
|
||||
imp [-width / 2 - 1] += (imp_t) error;
|
||||
}
|
||||
|
||||
if ( res > 2 )
|
||||
{
|
||||
// second half is mirror-image
|
||||
const imp_t* rev = imp - width - 1;
|
||||
for ( int nn = (res / 2 - 1) * width - 1; nn--; )
|
||||
*imp++ = *--rev;
|
||||
*imp++ = (imp_t) unit;
|
||||
}
|
||||
|
||||
// copy to odd offset
|
||||
*imp++ = (imp_t) unit;
|
||||
memcpy( imp, imp_in, (res * width - 1) * sizeof *imp );
|
||||
|
||||
/*
|
||||
for ( int i = 0; i < res; i++ )
|
||||
{
|
||||
for ( int j = 0; j < width; j++ )
|
||||
printf( "%6d,", imp_in [i * width + j] - 0x8000 );
|
||||
printf( "\n" );
|
||||
}*/
|
||||
}
|
||||
|
||||
const int max_res = 1 << blip_res_bits_;
|
||||
|
||||
void Blip_Impulse_::fine_volume_unit()
|
||||
{
|
||||
// to do: find way of merging in-place without temporary buffer
|
||||
|
||||
imp_t temp [max_res * 2 * Blip_Buffer::widest_impulse_];
|
||||
scale_impulse( (offset & 0xffff) << fine_bits, temp );
|
||||
imp_t* imp2 = impulses + res * 2 * width;
|
||||
scale_impulse( offset & 0xffff, imp2 );
|
||||
|
||||
// merge impulses
|
||||
imp_t* imp = impulses;
|
||||
imp_t* src2 = temp;
|
||||
for ( int n = res / 2 * 2 * width; n--; )
|
||||
{
|
||||
*imp++ = *imp2++;
|
||||
*imp++ = *imp2++;
|
||||
*imp++ = *src2++;
|
||||
*imp++ = *src2++;
|
||||
}
|
||||
}
|
||||
|
||||
void Blip_Impulse_::volume_unit( double new_unit )
|
||||
{
|
||||
if ( new_unit == volume_unit_ )
|
||||
return;
|
||||
|
||||
if ( generate )
|
||||
treble_eq( blip_eq_t( -8.87, 8800, 44100 ) );
|
||||
|
||||
volume_unit_ = new_unit;
|
||||
|
||||
offset = 0x10001 * (unsigned long) floor( volume_unit_ * 0x10000 + 0.5 );
|
||||
|
||||
if ( fine_bits )
|
||||
fine_volume_unit();
|
||||
else
|
||||
scale_impulse( offset & 0xffff, impulses );
|
||||
}
|
||||
|
||||
static const double pi = 3.1415926535897932384626433832795029L;
|
||||
|
||||
void Blip_Impulse_::treble_eq( const blip_eq_t& new_eq )
|
||||
{
|
||||
if ( !generate && new_eq.treble == eq.treble && new_eq.cutoff == eq.cutoff &&
|
||||
new_eq.sample_rate == eq.sample_rate )
|
||||
return; // already calculated with same parameters
|
||||
|
||||
generate = false;
|
||||
eq = new_eq;
|
||||
|
||||
double treble = pow( 10.0, 1.0 / 20 * eq.treble ); // dB (-6dB = 0.50)
|
||||
if ( treble < 0.000005 )
|
||||
treble = 0.000005;
|
||||
|
||||
const double treble_freq = 22050.0; // treble level at 22 kHz harmonic
|
||||
const double sample_rate = eq.sample_rate;
|
||||
const double pt = treble_freq * 2 / sample_rate;
|
||||
double cutoff = eq.cutoff * 2 / sample_rate;
|
||||
if ( cutoff >= pt * 0.95 || cutoff >= 0.95 )
|
||||
{
|
||||
cutoff = 0.5;
|
||||
treble = 1.0;
|
||||
}
|
||||
|
||||
// DSF Synthesis (See T. Stilson & J. Smith (1996),
|
||||
// Alias-free digital synthesis of classic analog waveforms)
|
||||
|
||||
// reduce adjacent impulse interference by using small part of wide impulse
|
||||
const double n_harm = 4096;
|
||||
const double rolloff = pow( treble, 1.0 / (n_harm * pt - n_harm * cutoff) );
|
||||
const double rescale = 1.0 / pow( rolloff, n_harm * cutoff );
|
||||
|
||||
const double pow_a_n = rescale * pow( rolloff, n_harm );
|
||||
const double pow_a_nc = rescale * pow( rolloff, n_harm * cutoff );
|
||||
|
||||
double total = 0.0;
|
||||
const double to_angle = pi / 2 / n_harm / max_res;
|
||||
|
||||
float buf [max_res * (Blip_Buffer::widest_impulse_ - 2) / 2];
|
||||
const int size = max_res * (width - 2) / 2;
|
||||
for ( int i = size; i--; )
|
||||
{
|
||||
double angle = (i * 2 + 1) * to_angle;
|
||||
|
||||
// equivalent
|
||||
//double y = dsf( angle, n_harm * cutoff, 1.0 );
|
||||
//y -= rescale * dsf( angle, n_harm * cutoff, rolloff );
|
||||
//y += rescale * dsf( angle, n_harm, rolloff );
|
||||
|
||||
const double cos_angle = cos( angle );
|
||||
const double cos_nc_angle = cos( n_harm * cutoff * angle );
|
||||
const double cos_nc1_angle = cos( (n_harm * cutoff - 1.0) * angle );
|
||||
|
||||
double b = 2.0 - 2.0 * cos_angle;
|
||||
double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;
|
||||
|
||||
double d = 1.0 + rolloff * (rolloff - 2.0 * cos_angle);
|
||||
double c = pow_a_n * rolloff * cos( (n_harm - 1.0) * angle ) -
|
||||
pow_a_n * cos( n_harm * angle ) -
|
||||
pow_a_nc * rolloff * cos_nc1_angle +
|
||||
pow_a_nc * cos_nc_angle;
|
||||
|
||||
// optimization of a / b + c / d
|
||||
double y = (a * d + c * b) / (b * d);
|
||||
|
||||
// fixed window which affects wider impulses more
|
||||
if ( width > 12 )
|
||||
{
|
||||
double window = cos( n_harm / 1.25 / Blip_Buffer::widest_impulse_ * angle );
|
||||
y *= window * window;
|
||||
}
|
||||
|
||||
total += (float) y;
|
||||
buf [i] = (float) y;
|
||||
}
|
||||
|
||||
// integrate runs of length 'max_res'
|
||||
double factor = impulse_amp * 0.5 / total; // 0.5 accounts for other mirrored half
|
||||
imp_t* imp = impulse;
|
||||
const int step = max_res / res;
|
||||
int offset = res > 1 ? max_res : max_res / 2;
|
||||
for ( int n = res / 2 + 1; n--; offset -= step )
|
||||
{
|
||||
for ( int w = -width / 2; w < width / 2; w++ )
|
||||
{
|
||||
double sum = 0;
|
||||
for ( int i = max_res; i--; )
|
||||
{
|
||||
int index = w * max_res + offset + i;
|
||||
if ( index < 0 )
|
||||
index = -index - 1;
|
||||
if ( index < size )
|
||||
sum += buf [index];
|
||||
}
|
||||
*imp++ = (imp_t) floor( sum * factor + (impulse_offset + 0.5) );
|
||||
}
|
||||
}
|
||||
|
||||
// rescale
|
||||
double unit = volume_unit_;
|
||||
if ( unit >= 0 )
|
||||
{
|
||||
volume_unit_ = -1;
|
||||
volume_unit( unit );
|
||||
}
|
||||
}
|
||||
|
||||
void Blip_Buffer::remove_samples( long count )
|
||||
{
|
||||
require( buffer_ ); // sample rate must have been set
|
||||
|
||||
if ( !count ) // optimization
|
||||
return;
|
||||
|
||||
remove_silence( count );
|
||||
|
||||
// Allows synthesis slightly past time passed to end_frame(), as long as it's
|
||||
// not more than an output sample.
|
||||
// to do: kind of hacky, could add run_until() which keeps track of extra synthesis
|
||||
int const copy_extra = 1;
|
||||
|
||||
// copy remaining samples to beginning and clear old samples
|
||||
long remain = samples_avail() + widest_impulse_ + copy_extra;
|
||||
if ( count >= remain )
|
||||
memmove( buffer_, buffer_ + count, remain * sizeof (buf_t_) );
|
||||
else
|
||||
memcpy( buffer_, buffer_ + count, remain * sizeof (buf_t_) );
|
||||
memset( buffer_ + remain, sample_offset_ & 0xFF, count * sizeof (buf_t_) );
|
||||
}
|
||||
|
||||
#include BLARGG_ENABLE_OPTIMIZER
|
||||
|
||||
long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, bool stereo )
|
||||
{
|
||||
require( buffer_ ); // sample rate must have been set
|
||||
|
||||
long count = samples_avail();
|
||||
if ( count > max_samples )
|
||||
count = max_samples;
|
||||
|
||||
if ( !count )
|
||||
return 0; // optimization
|
||||
|
||||
int sample_offset_ = this->sample_offset_;
|
||||
int bass_shift = this->bass_shift;
|
||||
buf_t_* buf = buffer_;
|
||||
long accum = reader_accum;
|
||||
|
||||
if ( !stereo )
|
||||
{
|
||||
for ( long n = count; n--; )
|
||||
{
|
||||
long s = accum >> accum_fract;
|
||||
accum -= accum >> bass_shift;
|
||||
accum += (long (*buf++) - sample_offset_) << accum_fract;
|
||||
*out++ = (blip_sample_t) s;
|
||||
|
||||
// clamp sample
|
||||
if ( (BOOST::int16_t) s != s )
|
||||
out [-1] = blip_sample_t (0x7FFF - (s >> 24));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( long n = count; n--; )
|
||||
{
|
||||
long s = accum >> accum_fract;
|
||||
accum -= accum >> bass_shift;
|
||||
accum += (long (*buf++) - sample_offset_) << accum_fract;
|
||||
*out = (blip_sample_t) s;
|
||||
out += 2;
|
||||
|
||||
// clamp sample
|
||||
if ( (BOOST::int16_t) s != s )
|
||||
out [-2] = blip_sample_t (0x7FFF - (s >> 24));
|
||||
}
|
||||
}
|
||||
|
||||
reader_accum = accum;
|
||||
|
||||
remove_samples( count );
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void Blip_Buffer::mix_samples( const blip_sample_t* in, long count )
|
||||
{
|
||||
buf_t_* buf = &buffer_ [(offset_ >> BLIP_BUFFER_ACCURACY) + (widest_impulse_ / 2 - 1)];
|
||||
|
||||
int prev = 0;
|
||||
while ( count-- )
|
||||
{
|
||||
int s = *in++;
|
||||
*buf += s - prev;
|
||||
prev = s;
|
||||
++buf;
|
||||
}
|
||||
*buf -= *--in;
|
||||
}
|
||||
|
||||
259
plugins/papu/gb_apu/Blip_Buffer.h
Normal file
@@ -0,0 +1,259 @@
|
||||
|
||||
// Buffer of sound samples into which band-limited waveforms can be synthesized
|
||||
// using Blip_Wave or Blip_Synth.
|
||||
|
||||
// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
|
||||
|
||||
#ifndef BLIP_BUFFER_H
|
||||
#define BLIP_BUFFER_H
|
||||
|
||||
#include "blargg_common.h"
|
||||
|
||||
class Blip_Reader;
|
||||
|
||||
// Source time unit.
|
||||
typedef long blip_time_t;
|
||||
|
||||
// Type of sample produced. Signed 16-bit format.
|
||||
typedef BOOST::int16_t blip_sample_t;
|
||||
|
||||
// Make buffer as large as possible (currently about 65000 samples)
|
||||
const int blip_default_length = 0;
|
||||
|
||||
typedef unsigned long blip_resampled_time_t; // not documented
|
||||
|
||||
class Blip_Buffer {
|
||||
public:
|
||||
// Construct an empty buffer.
|
||||
Blip_Buffer();
|
||||
~Blip_Buffer();
|
||||
|
||||
// Set output sample rate and buffer length in milliseconds (1/1000 sec),
|
||||
// then clear buffer. If length is not specified, make as large as possible.
|
||||
// If there is insufficient memory for the buffer, sets the buffer length
|
||||
// to 0 and returns error string (or propagates exception if compiler supports it).
|
||||
blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = blip_default_length );
|
||||
|
||||
// Length of buffer, in milliseconds
|
||||
int length() const;
|
||||
|
||||
// Current output sample rate
|
||||
long sample_rate() const;
|
||||
|
||||
// Number of source time units per second
|
||||
void clock_rate( long );
|
||||
long clock_rate() const;
|
||||
|
||||
// Set frequency at which high-pass filter attenuation passes -3dB
|
||||
void bass_freq( int frequency );
|
||||
|
||||
// Remove all available samples and clear buffer to silence. If 'entire_buffer' is
|
||||
// false, just clear out any samples waiting rather than the entire buffer.
|
||||
void clear( bool entire_buffer = true );
|
||||
|
||||
// End current time frame of specified duration and make its samples available
|
||||
// (along with any still-unread samples) for reading with read_samples(). Begin
|
||||
// a new time frame at the end of the current frame. All transitions must have
|
||||
// been added before 'time'.
|
||||
void end_frame( blip_time_t time );
|
||||
|
||||
// Number of samples available for reading with read_samples()
|
||||
long samples_avail() const;
|
||||
|
||||
// Read at most 'max_samples' out of buffer into 'dest', removing them from from
|
||||
// the buffer. Return number of samples actually read and removed. If stereo is
|
||||
// true, increment 'dest' one extra time after writing each sample, to allow
|
||||
// easy interleving of two channels into a stereo output buffer.
|
||||
long read_samples( blip_sample_t* dest, long max_samples, bool stereo = false );
|
||||
|
||||
// Remove 'count' samples from those waiting to be read
|
||||
void remove_samples( long count );
|
||||
|
||||
// Number of samples delay from synthesis to samples read out
|
||||
int output_latency() const;
|
||||
|
||||
// Beta features
|
||||
|
||||
// Number of raw samples that can be mixed within frame of specified duration
|
||||
long count_samples( blip_time_t duration ) const;
|
||||
|
||||
// Mix 'count' samples from 'buf' into buffer.
|
||||
void mix_samples( const blip_sample_t* buf, long count );
|
||||
|
||||
// Count number of clocks needed until 'count' samples will be available.
|
||||
// If buffer can't even hold 'count' samples, returns number of clocks until
|
||||
// buffer is full.
|
||||
blip_time_t count_clocks( long count ) const;
|
||||
|
||||
|
||||
// not documented yet
|
||||
|
||||
void remove_silence( long count );
|
||||
|
||||
blip_resampled_time_t resampled_time( blip_time_t t ) const
|
||||
{
|
||||
return t * blip_resampled_time_t (factor_) + offset_;
|
||||
}
|
||||
|
||||
blip_resampled_time_t clock_rate_factor( long clock_rate ) const;
|
||||
|
||||
blip_resampled_time_t resampled_duration( int t ) const
|
||||
{
|
||||
return t * blip_resampled_time_t (factor_);
|
||||
}
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
Blip_Buffer( const Blip_Buffer& );
|
||||
Blip_Buffer& operator = ( const Blip_Buffer& );
|
||||
|
||||
// Don't use the following members. They are public only for technical reasons.
|
||||
public:
|
||||
enum { sample_offset_ = 0x7F7F }; // repeated byte allows memset to clear buffer
|
||||
enum { widest_impulse_ = 24 };
|
||||
typedef BOOST::uint16_t buf_t_;
|
||||
|
||||
unsigned long factor_;
|
||||
blip_resampled_time_t offset_;
|
||||
buf_t_* buffer_;
|
||||
unsigned buffer_size_;
|
||||
private:
|
||||
long reader_accum;
|
||||
int bass_shift;
|
||||
long samples_per_sec;
|
||||
long clocks_per_sec;
|
||||
int bass_freq_;
|
||||
int length_;
|
||||
|
||||
enum { accum_fract = 15 }; // less than 16 to give extra sample range
|
||||
|
||||
friend class Blip_Reader;
|
||||
};
|
||||
|
||||
// Low-pass equalization parameters (see notes.txt)
|
||||
class blip_eq_t {
|
||||
public:
|
||||
blip_eq_t( double treble = 0 );
|
||||
blip_eq_t( double treble, long cutoff, long sample_rate );
|
||||
private:
|
||||
double treble;
|
||||
long cutoff;
|
||||
long sample_rate;
|
||||
friend class Blip_Impulse_;
|
||||
};
|
||||
|
||||
// not documented yet (see Multi_Buffer.cpp for an example of use)
|
||||
class Blip_Reader {
|
||||
const Blip_Buffer::buf_t_* buf;
|
||||
long accum;
|
||||
#ifdef __MWERKS__
|
||||
void operator = ( struct foobar ); // helps optimizer
|
||||
#endif
|
||||
public:
|
||||
// avoid anything which might cause optimizer to put object in memory
|
||||
|
||||
int begin( Blip_Buffer& blip_buf ) {
|
||||
buf = blip_buf.buffer_;
|
||||
accum = blip_buf.reader_accum;
|
||||
return blip_buf.bass_shift;
|
||||
}
|
||||
|
||||
int read() const {
|
||||
return accum >> Blip_Buffer::accum_fract;
|
||||
}
|
||||
|
||||
void next( int bass_shift = 9 ) {
|
||||
accum -= accum >> bass_shift;
|
||||
accum += ((long) *buf++ - Blip_Buffer::sample_offset_) << Blip_Buffer::accum_fract;
|
||||
}
|
||||
|
||||
void end( Blip_Buffer& blip_buf ) {
|
||||
blip_buf.reader_accum = accum;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// End of public interface
|
||||
|
||||
#ifndef BLIP_BUFFER_ACCURACY
|
||||
#define BLIP_BUFFER_ACCURACY 16
|
||||
#endif
|
||||
|
||||
const int blip_res_bits_ = 5;
|
||||
|
||||
typedef BOOST::uint32_t blip_pair_t_;
|
||||
|
||||
class Blip_Impulse_ {
|
||||
typedef BOOST::uint16_t imp_t;
|
||||
|
||||
blip_eq_t eq;
|
||||
double volume_unit_;
|
||||
imp_t* impulses;
|
||||
imp_t* impulse;
|
||||
int width;
|
||||
int fine_bits;
|
||||
int res;
|
||||
bool generate;
|
||||
|
||||
void fine_volume_unit();
|
||||
void scale_impulse( int unit, imp_t* ) const;
|
||||
public:
|
||||
Blip_Buffer* buf;
|
||||
BOOST::uint32_t offset;
|
||||
|
||||
void init( blip_pair_t_* impulses, int width, int res, int fine_bits = 0 );
|
||||
void volume_unit( double );
|
||||
void treble_eq( const blip_eq_t& );
|
||||
};
|
||||
|
||||
inline blip_eq_t::blip_eq_t( double t ) :
|
||||
treble( t ), cutoff( 0 ), sample_rate( 44100 ) {
|
||||
}
|
||||
|
||||
inline blip_eq_t::blip_eq_t( double t, long c, long sr ) :
|
||||
treble( t ), cutoff( c ), sample_rate( sr ) {
|
||||
}
|
||||
|
||||
inline int Blip_Buffer::length() const {
|
||||
return length_;
|
||||
}
|
||||
|
||||
inline long Blip_Buffer::samples_avail() const {
|
||||
return long (offset_ >> BLIP_BUFFER_ACCURACY);
|
||||
}
|
||||
|
||||
inline long Blip_Buffer::sample_rate() const {
|
||||
return samples_per_sec;
|
||||
}
|
||||
|
||||
inline void Blip_Buffer::end_frame( blip_time_t t ) {
|
||||
offset_ += t * factor_;
|
||||
assert(( "Blip_Buffer::end_frame(): Frame went past end of buffer",
|
||||
samples_avail() <= (long) buffer_size_ ));
|
||||
}
|
||||
|
||||
inline void Blip_Buffer::remove_silence( long count ) {
|
||||
assert(( "Blip_Buffer::remove_silence(): Tried to remove more samples than available",
|
||||
count <= samples_avail() ));
|
||||
offset_ -= blip_resampled_time_t (count) << BLIP_BUFFER_ACCURACY;
|
||||
}
|
||||
|
||||
inline int Blip_Buffer::output_latency() const {
|
||||
return widest_impulse_ / 2;
|
||||
}
|
||||
|
||||
inline long Blip_Buffer::clock_rate() const {
|
||||
return clocks_per_sec;
|
||||
}
|
||||
|
||||
inline void Blip_Buffer::clock_rate( long cps )
|
||||
{
|
||||
clocks_per_sec = cps;
|
||||
factor_ = clock_rate_factor( cps );
|
||||
}
|
||||
|
||||
#include "Blip_Synth.h"
|
||||
|
||||
#endif
|
||||
|
||||
208
plugins/papu/gb_apu/Blip_Synth.h
Normal file
@@ -0,0 +1,208 @@
|
||||
|
||||
// Blip_Synth and Blip_Wave are waveform transition synthesizers for adding
|
||||
// waveforms to a Blip_Buffer.
|
||||
|
||||
// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
|
||||
|
||||
#ifndef BLIP_SYNTH_H
|
||||
#define BLIP_SYNTH_H
|
||||
|
||||
#ifndef BLIP_BUFFER_H
|
||||
#include "Blip_Buffer.h"
|
||||
#endif
|
||||
|
||||
// Quality level. Higher levels are slower, and worse in a few cases.
|
||||
// Use blip_good_quality as a starting point.
|
||||
const int blip_low_quality = 1;
|
||||
const int blip_med_quality = 2;
|
||||
const int blip_good_quality = 3;
|
||||
const int blip_high_quality = 4;
|
||||
|
||||
// Blip_Synth is a transition waveform synthesizer which adds band-limited
|
||||
// offsets (transitions) into a Blip_Buffer. For a simpler interface, use
|
||||
// Blip_Wave (below).
|
||||
//
|
||||
// Range specifies the greatest expected offset that will occur. For a
|
||||
// waveform that goes between +amp and -amp, range should be amp * 2 (half
|
||||
// that if it only goes between +amp and 0). When range is large, a higher
|
||||
// accuracy scheme is used; to force this even when range is small, pass
|
||||
// the negative of range (i.e. -range).
|
||||
template<int quality,int range>
|
||||
class Blip_Synth {
|
||||
BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 );
|
||||
BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 );
|
||||
enum {
|
||||
abs_range = (range < 0) ? -range : range,
|
||||
fine_mode = (range > 512 || range < 0),
|
||||
width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_),
|
||||
res = 1 << blip_res_bits_,
|
||||
impulse_size = width / 2 * (fine_mode + 1),
|
||||
base_impulses_size = width / 2 * (res / 2 + 1),
|
||||
fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 :
|
||||
abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 :
|
||||
abs_range <= 2048 ? 7 : 8) : 0)
|
||||
};
|
||||
blip_pair_t_ impulses [impulse_size * res * 2 + base_impulses_size];
|
||||
Blip_Impulse_ impulse;
|
||||
void init() { impulse.init( impulses, width, res, fine_bits ); }
|
||||
public:
|
||||
Blip_Synth() { init(); }
|
||||
Blip_Synth( double volume ) { init(); this->volume( volume ); }
|
||||
|
||||
// Configure low-pass filter (see notes.txt). Not optimized for real-time control
|
||||
void treble_eq( const blip_eq_t& eq ) { impulse.treble_eq( eq ); }
|
||||
|
||||
// Set volume of a transition at amplitude 'range' by setting volume_unit
|
||||
// to v / range
|
||||
void volume( double v ) { impulse.volume_unit( v * (1.0 / abs_range) ); }
|
||||
|
||||
// Set base volume unit of transitions, where 1.0 is a full swing between the
|
||||
// positive and negative extremes. Not optimized for real-time control.
|
||||
void volume_unit( double unit ) { impulse.volume_unit( unit ); }
|
||||
|
||||
// Default Blip_Buffer used for output when none is specified for a given call
|
||||
Blip_Buffer* output() const { return impulse.buf; }
|
||||
void output( Blip_Buffer* b ) { impulse.buf = b; }
|
||||
|
||||
// Add an amplitude offset (transition) with a magnitude of delta * volume_unit
|
||||
// into the specified buffer (default buffer if none specified) at the
|
||||
// specified source time. Delta can be positive or negative. To increase
|
||||
// performance by inlining code at the call site, use offset_inline().
|
||||
void offset( blip_time_t, int delta, Blip_Buffer* ) const;
|
||||
|
||||
void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
|
||||
void offset_resampled( blip_resampled_time_t t, int o ) const {
|
||||
offset_resampled( t, o, impulse.buf );
|
||||
}
|
||||
void offset( blip_time_t t, int delta ) const {
|
||||
offset( t, delta, impulse.buf );
|
||||
}
|
||||
void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const {
|
||||
offset_resampled( time * buf->factor_ + buf->offset_, delta, buf );
|
||||
}
|
||||
void offset_inline( blip_time_t time, int delta ) const {
|
||||
offset_inline( time, delta, impulse.buf );
|
||||
}
|
||||
};
|
||||
|
||||
// Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer.
|
||||
// A wave is built from a series of delays and new amplitudes. This provides a
|
||||
// simpler interface than Blip_Synth, nothing more.
|
||||
template<int quality,int range>
|
||||
class Blip_Wave {
|
||||
Blip_Synth<quality,range> synth;
|
||||
blip_time_t time_;
|
||||
int last_amp;
|
||||
void init() { time_ = 0; last_amp = 0; }
|
||||
public:
|
||||
// Start wave at time 0 and amplitude 0
|
||||
Blip_Wave() { init(); }
|
||||
Blip_Wave( double volume ) { init(); this->volume( volume ); }
|
||||
|
||||
// See Blip_Synth for description
|
||||
void volume( double v ) { synth.volume( v ); }
|
||||
void volume_unit( double v ) { synth.volume_unit( v ); }
|
||||
void treble_eq( const blip_eq_t& eq){ synth.treble_eq( eq ); }
|
||||
Blip_Buffer* output() const { return synth.output(); }
|
||||
void output( Blip_Buffer* b ) { synth.output( b ); if ( !b ) time_ = last_amp = 0; }
|
||||
|
||||
// Current time in frame
|
||||
blip_time_t time() const { return time_; }
|
||||
void time( blip_time_t t ) { time_ = t; }
|
||||
|
||||
// Current amplitude of wave
|
||||
int amplitude() const { return last_amp; }
|
||||
void amplitude( int );
|
||||
|
||||
// Move forward by 't' time units
|
||||
void delay( blip_time_t t ) { time_ += t; }
|
||||
|
||||
// End time frame of specified duration. Localize time to new frame.
|
||||
// If wave hadn't been run to end of frame, start it at beginning of new frame.
|
||||
void end_frame( blip_time_t duration )
|
||||
{
|
||||
time_ -= duration;
|
||||
if ( time_ < 0 )
|
||||
time_ = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// End of public interface
|
||||
|
||||
template<int quality,int range>
|
||||
void Blip_Wave<quality,range>::amplitude( int amp ) {
|
||||
int delta = amp - last_amp;
|
||||
last_amp = amp;
|
||||
synth.offset_inline( time_, delta );
|
||||
}
|
||||
|
||||
template<int quality,int range>
|
||||
inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
|
||||
int delta, Blip_Buffer* blip_buf ) const
|
||||
{
|
||||
typedef blip_pair_t_ pair_t;
|
||||
|
||||
unsigned sample_index = (time >> BLIP_BUFFER_ACCURACY) & ~1;
|
||||
assert(( "Blip_Synth/Blip_wave: Went past end of buffer",
|
||||
sample_index < blip_buf->buffer_size_ ));
|
||||
enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 };
|
||||
pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index];
|
||||
|
||||
enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ };
|
||||
enum { mask = res * 2 - 1 };
|
||||
const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size];
|
||||
|
||||
pair_t offset = impulse.offset * delta;
|
||||
|
||||
if ( !fine_bits )
|
||||
{
|
||||
// normal mode
|
||||
for ( int n = width / 4; n; --n )
|
||||
{
|
||||
pair_t t0 = buf [0] - offset;
|
||||
pair_t t1 = buf [1] - offset;
|
||||
|
||||
t0 += imp [0] * delta;
|
||||
t1 += imp [1] * delta;
|
||||
imp += 2;
|
||||
|
||||
buf [0] = t0;
|
||||
buf [1] = t1;
|
||||
buf += 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// fine mode
|
||||
enum { sub_range = 1 << fine_bits };
|
||||
delta += sub_range / 2;
|
||||
int delta2 = (delta & (sub_range - 1)) - sub_range / 2;
|
||||
delta >>= fine_bits;
|
||||
|
||||
for ( int n = width / 4; n; --n )
|
||||
{
|
||||
pair_t t0 = buf [0] - offset;
|
||||
pair_t t1 = buf [1] - offset;
|
||||
|
||||
t0 += imp [0] * delta2;
|
||||
t0 += imp [1] * delta;
|
||||
|
||||
t1 += imp [2] * delta2;
|
||||
t1 += imp [3] * delta;
|
||||
|
||||
imp += 4;
|
||||
|
||||
buf [0] = t0;
|
||||
buf [1] = t1;
|
||||
buf += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<int quality,int range>
|
||||
void Blip_Synth<quality,range>::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const {
|
||||
offset_resampled( time * buf->factor_ + buf->offset_, delta, buf );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
261
plugins/papu/gb_apu/Gb_Apu.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
|
||||
// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/libs/
|
||||
|
||||
#include "Gb_Apu.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details. You should have received a copy of the GNU Lesser General
|
||||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include BLARGG_SOURCE_BEGIN
|
||||
|
||||
Gb_Apu::Gb_Apu()
|
||||
{
|
||||
square1.synth = &square_synth;
|
||||
square2.synth = &square_synth;
|
||||
square1.has_sweep = true;
|
||||
wave.synth = &other_synth;
|
||||
noise.synth = &other_synth;
|
||||
|
||||
oscs [0] = &square1;
|
||||
oscs [1] = &square2;
|
||||
oscs [2] = &wave;
|
||||
oscs [3] = &noise;
|
||||
|
||||
volume( 1.0 );
|
||||
reset();
|
||||
}
|
||||
|
||||
Gb_Apu::~Gb_Apu()
|
||||
{
|
||||
}
|
||||
|
||||
void Gb_Apu::treble_eq( const blip_eq_t& eq )
|
||||
{
|
||||
square_synth.treble_eq( eq );
|
||||
other_synth.treble_eq( eq );
|
||||
}
|
||||
|
||||
void Gb_Apu::volume( double vol )
|
||||
{
|
||||
vol *= 0.60 / osc_count;
|
||||
square_synth.volume( vol );
|
||||
other_synth.volume( vol );
|
||||
}
|
||||
|
||||
void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
|
||||
{
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
osc_output( i, center, left, right );
|
||||
}
|
||||
|
||||
void Gb_Apu::reset()
|
||||
{
|
||||
next_frame_time = 0;
|
||||
last_time = 0;
|
||||
frame_count = 0;
|
||||
stereo_found = false;
|
||||
|
||||
square1.reset();
|
||||
square2.reset();
|
||||
wave.reset();
|
||||
noise.reset();
|
||||
|
||||
memset( regs, 0, sizeof regs );
|
||||
}
|
||||
|
||||
void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
|
||||
{
|
||||
require( (unsigned) index < osc_count );
|
||||
|
||||
Gb_Osc& osc = *oscs [index];
|
||||
if ( center && !left && !right )
|
||||
{
|
||||
// mono
|
||||
left = center;
|
||||
right = center;
|
||||
}
|
||||
else
|
||||
{
|
||||
// must be silenced or stereo
|
||||
require( (!left && !right) || (left && right) );
|
||||
}
|
||||
osc.outputs [1] = right;
|
||||
osc.outputs [2] = left;
|
||||
osc.outputs [3] = center;
|
||||
osc.output = osc.outputs [osc.output_select];
|
||||
}
|
||||
|
||||
void Gb_Apu::run_until( gb_time_t end_time )
|
||||
{
|
||||
require( end_time >= last_time ); // end_time must not be before previous time
|
||||
if ( end_time == last_time )
|
||||
return;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
gb_time_t time = next_frame_time;
|
||||
if ( time > end_time )
|
||||
time = end_time;
|
||||
|
||||
// run oscillators
|
||||
for ( int i = 0; i < osc_count; ++i ) {
|
||||
Gb_Osc& osc = *oscs [i];
|
||||
if ( osc.output ) {
|
||||
if ( osc.output != osc.outputs [3] )
|
||||
stereo_found = true;
|
||||
osc.run( last_time, time );
|
||||
}
|
||||
}
|
||||
last_time = time;
|
||||
|
||||
if ( time == end_time )
|
||||
break;
|
||||
|
||||
next_frame_time += 4194304 / 256; // 256 Hz
|
||||
|
||||
// 256 Hz actions
|
||||
square1.clock_length();
|
||||
square2.clock_length();
|
||||
wave.clock_length();
|
||||
noise.clock_length();
|
||||
|
||||
frame_count = (frame_count + 1) & 3;
|
||||
if ( frame_count == 0 ) {
|
||||
// 64 Hz actions
|
||||
square1.clock_envelope();
|
||||
square2.clock_envelope();
|
||||
noise.clock_envelope();
|
||||
}
|
||||
|
||||
if ( frame_count & 1 )
|
||||
square1.clock_sweep(); // 128 Hz action
|
||||
}
|
||||
}
|
||||
|
||||
bool Gb_Apu::end_frame( gb_time_t end_time )
|
||||
{
|
||||
if ( end_time > last_time )
|
||||
run_until( end_time );
|
||||
|
||||
assert( next_frame_time >= end_time );
|
||||
next_frame_time -= end_time;
|
||||
|
||||
assert( last_time >= end_time );
|
||||
last_time -= end_time;
|
||||
|
||||
bool result = stereo_found;
|
||||
stereo_found = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data )
|
||||
{
|
||||
require( (unsigned) data < 0x100 );
|
||||
|
||||
int reg = addr - start_addr;
|
||||
if ( (unsigned) reg >= register_count )
|
||||
return;
|
||||
|
||||
run_until( time );
|
||||
|
||||
regs [reg] = data;
|
||||
|
||||
if ( addr < 0xff24 )
|
||||
{
|
||||
// oscillator
|
||||
int index = reg / 5;
|
||||
oscs [index]->write_register( reg - index * 5, data );
|
||||
}
|
||||
// added
|
||||
else if ( addr == 0xff24 )
|
||||
{
|
||||
int global_volume = data & 7;
|
||||
int old_volume = square1.global_volume;
|
||||
if ( old_volume != global_volume )
|
||||
{
|
||||
int any_enabled = false;
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
{
|
||||
Gb_Osc& osc = *oscs [i];
|
||||
if ( osc.enabled )
|
||||
{
|
||||
if ( osc.last_amp )
|
||||
{
|
||||
int new_amp = osc.last_amp * global_volume / osc.global_volume;
|
||||
if ( osc.output )
|
||||
square_synth.offset( time, new_amp - osc.last_amp, osc.output );
|
||||
osc.last_amp = new_amp;
|
||||
}
|
||||
any_enabled |= osc.volume;
|
||||
}
|
||||
osc.global_volume = global_volume;
|
||||
}
|
||||
|
||||
if ( !any_enabled && square1.outputs [3] )
|
||||
square_synth.offset( time, (global_volume - old_volume) * 15 * 2, square1.outputs [3] );
|
||||
}
|
||||
}
|
||||
|
||||
else if ( addr == 0xff25 || addr == 0xff26 )
|
||||
{
|
||||
int mask = (regs [0xff26 - start_addr] & 0x80) ? ~0 : 0;
|
||||
int flags = regs [0xff25 - start_addr] & mask;
|
||||
|
||||
// left/right assignments
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
{
|
||||
Gb_Osc& osc = *oscs [i];
|
||||
osc.enabled &= mask;
|
||||
int bits = flags >> i;
|
||||
Blip_Buffer* old_output = osc.output;
|
||||
osc.output_select = (bits >> 3 & 2) | (bits & 1);
|
||||
osc.output = osc.outputs [osc.output_select];
|
||||
if ( osc.output != old_output && osc.last_amp )
|
||||
{
|
||||
if ( old_output )
|
||||
square_synth.offset( time, -osc.last_amp, old_output );
|
||||
osc.last_amp = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( addr >= 0xff30 )
|
||||
{
|
||||
int index = (addr & 0x0f) * 2;
|
||||
wave.wave [index] = data >> 4;
|
||||
wave.wave [index + 1] = data & 0x0f;
|
||||
}
|
||||
}
|
||||
|
||||
int Gb_Apu::read_register( gb_time_t time, gb_addr_t addr )
|
||||
{
|
||||
// function now takes actual address, i.e. 0xFFXX
|
||||
require( start_addr <= addr && addr <= end_addr );
|
||||
|
||||
run_until( time );
|
||||
|
||||
int data = regs [addr - start_addr];
|
||||
|
||||
if ( addr == 0xff26 )
|
||||
{
|
||||
data &= 0xf0;
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
{
|
||||
const Gb_Osc& osc = *oscs [i];
|
||||
if ( osc.enabled && (osc.length || !osc.length_enabled) )
|
||||
data |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
84
plugins/papu/gb_apu/Gb_Apu.h
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
// Nintendo Game Boy PAPU sound chip emulator
|
||||
|
||||
// Gb_Snd_Emu 0.1.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
|
||||
|
||||
#ifndef GB_APU_H
|
||||
#define GB_APU_H
|
||||
|
||||
typedef long gb_time_t; // clock cycle count
|
||||
typedef unsigned gb_addr_t; // 16-bit address
|
||||
|
||||
#include "Gb_Oscs.h"
|
||||
|
||||
class Gb_Apu {
|
||||
public:
|
||||
Gb_Apu();
|
||||
~Gb_Apu();
|
||||
|
||||
// Set overall volume of all oscillators, where 1.0 is full volume
|
||||
void volume( double );
|
||||
|
||||
// Set treble equalization
|
||||
void treble_eq( const blip_eq_t& );
|
||||
|
||||
// Reset oscillators and internal state
|
||||
void reset();
|
||||
|
||||
// Assign all oscillator outputs to specified buffer(s). If buffer
|
||||
// is NULL, silence all oscillators.
|
||||
void output( Blip_Buffer* mono );
|
||||
void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
|
||||
|
||||
// Assign single oscillator output to buffer(s). Valid indicies are 0 to 3,
|
||||
// which refer to Square 1, Square 2, Wave, and Noise.
|
||||
// If buffer is NULL, silence oscillator.
|
||||
enum { osc_count = 4 };
|
||||
void osc_output( int index, Blip_Buffer* mono );
|
||||
void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
|
||||
|
||||
// Reads and writes at addr must satisfy start_addr <= addr <= end_addr
|
||||
enum { start_addr = 0xff10 };
|
||||
enum { end_addr = 0xff3f };
|
||||
enum { register_count = end_addr - start_addr + 1 };
|
||||
|
||||
// Write 'data' to address at specified time
|
||||
void write_register( gb_time_t, gb_addr_t, int data );
|
||||
|
||||
// Read from address at specified time
|
||||
int read_register( gb_time_t, gb_addr_t );
|
||||
|
||||
// Run all oscillators up to specified time, end current time frame, then
|
||||
// start a new frame at time 0. Return true if any oscillators added
|
||||
// sound to one of the left/right buffers, false if they only added
|
||||
// to the center buffer.
|
||||
bool end_frame( gb_time_t );
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
Gb_Apu( const Gb_Apu& );
|
||||
Gb_Apu& operator = ( const Gb_Apu& );
|
||||
|
||||
Gb_Osc* oscs [osc_count];
|
||||
gb_time_t next_frame_time;
|
||||
gb_time_t last_time;
|
||||
int frame_count;
|
||||
bool stereo_found;
|
||||
|
||||
Gb_Square square1;
|
||||
Gb_Square square2;
|
||||
Gb_Wave wave;
|
||||
Gb_Noise noise;
|
||||
BOOST::uint8_t regs [register_count];
|
||||
Gb_Square::Synth square_synth; // shared between squares
|
||||
Gb_Wave::Synth other_synth; // shared between wave and noise
|
||||
|
||||
void run_until( gb_time_t );
|
||||
};
|
||||
|
||||
inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, NULL, NULL ); }
|
||||
|
||||
inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, NULL, NULL ); }
|
||||
|
||||
#endif
|
||||
|
||||
451
plugins/papu/gb_apu/Gb_Oscs.cpp
Normal file
@@ -0,0 +1,451 @@
|
||||
|
||||
// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/libs/
|
||||
|
||||
#include "Gb_Apu.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details. You should have received a copy of the GNU Lesser General
|
||||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include BLARGG_SOURCE_BEGIN
|
||||
|
||||
const int trigger = 0x80;
|
||||
|
||||
// Gb_Osc
|
||||
|
||||
Gb_Osc::Gb_Osc()
|
||||
{
|
||||
output = NULL;
|
||||
outputs [0] = NULL;
|
||||
outputs [1] = NULL;
|
||||
outputs [2] = NULL;
|
||||
outputs [3] = NULL;
|
||||
}
|
||||
|
||||
void Gb_Osc::reset()
|
||||
{
|
||||
delay = 0;
|
||||
last_amp = 0;
|
||||
period = 2048;
|
||||
volume = 0;
|
||||
global_volume = 7; // added
|
||||
frequency = 0;
|
||||
length = 0;
|
||||
enabled = false;
|
||||
length_enabled = false;
|
||||
output_select = 3;
|
||||
output = outputs [output_select];
|
||||
}
|
||||
|
||||
void Gb_Osc::clock_length()
|
||||
{
|
||||
if ( length_enabled && length )
|
||||
--length;
|
||||
}
|
||||
|
||||
void Gb_Osc::write_register( int reg, int value )
|
||||
{
|
||||
if ( reg == 4 )
|
||||
length_enabled = value & 0x40;
|
||||
}
|
||||
|
||||
// Gb_Env
|
||||
|
||||
void Gb_Env::reset()
|
||||
{
|
||||
env_period = 0;
|
||||
env_dir = 0;
|
||||
env_delay = 0;
|
||||
new_volume = 0;
|
||||
Gb_Osc::reset();
|
||||
}
|
||||
|
||||
Gb_Env::Gb_Env()
|
||||
{
|
||||
}
|
||||
|
||||
void Gb_Env::clock_envelope()
|
||||
{
|
||||
if ( env_delay && !--env_delay )
|
||||
{
|
||||
env_delay = env_period;
|
||||
if ( env_dir )
|
||||
{
|
||||
if ( volume < 15 )
|
||||
++volume;
|
||||
}
|
||||
else if ( volume > 0 )
|
||||
{
|
||||
--volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gb_Env::write_register( int reg, int value )
|
||||
{
|
||||
if ( reg == 2 ) {
|
||||
env_period = value & 7;
|
||||
env_dir = value & 8;
|
||||
volume = new_volume = value >> 4;
|
||||
}
|
||||
else if ( reg == 4 && (value & trigger) ) {
|
||||
env_delay = env_period;
|
||||
volume = new_volume;
|
||||
enabled = true;
|
||||
}
|
||||
Gb_Osc::write_register( reg, value );
|
||||
}
|
||||
|
||||
// Gb_Square
|
||||
|
||||
void Gb_Square::reset()
|
||||
{
|
||||
phase = 1;
|
||||
duty = 1;
|
||||
|
||||
sweep_period = 0;
|
||||
sweep_delay = 0;
|
||||
sweep_shift = 0;
|
||||
sweep_dir = 0;
|
||||
sweep_freq = 0;
|
||||
|
||||
new_length = 0;
|
||||
|
||||
Gb_Env::reset();
|
||||
}
|
||||
|
||||
Gb_Square::Gb_Square()
|
||||
{
|
||||
has_sweep = false;
|
||||
}
|
||||
|
||||
void Gb_Square::clock_sweep()
|
||||
{
|
||||
if ( sweep_period && sweep_delay && !--sweep_delay )
|
||||
{
|
||||
sweep_delay = sweep_period;
|
||||
frequency = sweep_freq;
|
||||
|
||||
period = (2048 - frequency) * 4;
|
||||
|
||||
int offset = sweep_freq >> sweep_shift;
|
||||
if ( sweep_dir )
|
||||
offset = -offset;
|
||||
sweep_freq += offset;
|
||||
|
||||
if ( sweep_freq < 0 )
|
||||
{
|
||||
sweep_freq = 0;
|
||||
}
|
||||
else if ( sweep_freq >= 2048 )
|
||||
{
|
||||
sweep_delay = 0;
|
||||
sweep_freq = 2048; // stop sound output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gb_Square::write_register( int reg, int value )
|
||||
{
|
||||
static unsigned char const duty_table [4] = { 1, 2, 4, 6 };
|
||||
|
||||
switch ( reg )
|
||||
{
|
||||
case 0:
|
||||
sweep_period = (value >> 4) & 7; // changed
|
||||
sweep_shift = value & 7;
|
||||
sweep_dir = value & 0x08;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
new_length = length = 64 - (value & 0x3f);
|
||||
duty = duty_table [value >> 6];
|
||||
break;
|
||||
|
||||
case 3:
|
||||
frequency = (frequency & ~0xFF) + value;
|
||||
length = new_length;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
frequency = (value & 7) * 0x100 + (frequency & 0xFF);
|
||||
length = new_length;
|
||||
if ( value & trigger )
|
||||
{
|
||||
sweep_freq = frequency;
|
||||
if ( has_sweep && sweep_period && sweep_shift )
|
||||
{
|
||||
sweep_delay = 1;
|
||||
clock_sweep();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
period = (2048 - frequency) * 4;
|
||||
|
||||
Gb_Env::write_register( reg, value );
|
||||
}
|
||||
|
||||
void Gb_Square::run( gb_time_t time, gb_time_t end_time )
|
||||
{
|
||||
// to do: when frequency goes above 20000 Hz output should actually be 1/2 volume
|
||||
// rather than 0
|
||||
|
||||
if ( !enabled || (!length && length_enabled) || !volume || sweep_freq == 2048 ||
|
||||
!frequency || period < 27 )
|
||||
{
|
||||
if ( last_amp )
|
||||
{
|
||||
synth->offset( time, -last_amp, output );
|
||||
last_amp = 0;
|
||||
}
|
||||
delay = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int amp = (phase < duty) ? volume : -volume;
|
||||
amp *= global_volume;
|
||||
if ( amp != last_amp )
|
||||
{
|
||||
synth->offset( time, amp - last_amp, output );
|
||||
last_amp = amp;
|
||||
}
|
||||
|
||||
time += delay;
|
||||
if ( time < end_time )
|
||||
{
|
||||
Blip_Buffer* const output = this->output;
|
||||
const int duty = this->duty;
|
||||
int phase = this->phase;
|
||||
amp *= 2;
|
||||
do
|
||||
{
|
||||
phase = (phase + 1) & 7;
|
||||
if ( phase == 0 || phase == duty )
|
||||
{
|
||||
amp = -amp;
|
||||
synth->offset_inline( time, amp, output );
|
||||
}
|
||||
time += period;
|
||||
}
|
||||
while ( time < end_time );
|
||||
|
||||
this->phase = phase;
|
||||
last_amp = amp >> 1;
|
||||
}
|
||||
delay = time - end_time;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Gb_Wave
|
||||
|
||||
void Gb_Wave::reset()
|
||||
{
|
||||
volume_shift = 0;
|
||||
wave_pos = 0;
|
||||
new_length = 0;
|
||||
memset( wave, 0, sizeof wave );
|
||||
Gb_Osc::reset();
|
||||
}
|
||||
|
||||
Gb_Wave::Gb_Wave() {
|
||||
}
|
||||
|
||||
void Gb_Wave::write_register( int reg, int value )
|
||||
{
|
||||
switch ( reg )
|
||||
{
|
||||
case 0:
|
||||
new_enabled = value & 0x80;
|
||||
enabled &= new_enabled;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
new_length = length = 256 - value;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
volume = ((value >> 5) & 3);
|
||||
volume_shift = (volume - 1) & 7; // silence = 7
|
||||
break;
|
||||
|
||||
case 3:
|
||||
frequency = (frequency & ~0xFF) + value;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
frequency = (value & 7) * 0x100 + (frequency & 0xFF);
|
||||
if ( new_enabled && (value & trigger) )
|
||||
{
|
||||
wave_pos = 0;
|
||||
length = new_length;
|
||||
enabled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
period = (2048 - frequency) * 2;
|
||||
|
||||
Gb_Osc::write_register( reg, value );
|
||||
}
|
||||
|
||||
void Gb_Wave::run( gb_time_t time, gb_time_t end_time )
|
||||
{
|
||||
// to do: when frequency goes above 20000 Hz output should actually be 1/2 volume
|
||||
// rather than 0
|
||||
if ( !enabled || (!length && length_enabled) || !volume || !frequency || period < 7 )
|
||||
{
|
||||
if ( last_amp ) {
|
||||
synth->offset( time, -last_amp, output );
|
||||
last_amp = 0;
|
||||
}
|
||||
delay = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int const vol_factor = global_volume * 2;
|
||||
|
||||
// wave data or shift may have changed
|
||||
int diff = (wave [wave_pos] >> volume_shift) * vol_factor - last_amp;
|
||||
if ( diff )
|
||||
{
|
||||
last_amp += diff;
|
||||
synth->offset( time, diff, output );
|
||||
}
|
||||
|
||||
time += delay;
|
||||
if ( time < end_time )
|
||||
{
|
||||
int const volume_shift = this->volume_shift;
|
||||
int wave_pos = this->wave_pos;
|
||||
|
||||
do
|
||||
{
|
||||
wave_pos = unsigned (wave_pos + 1) % wave_size;
|
||||
int amp = (wave [wave_pos] >> volume_shift) * vol_factor;
|
||||
int delta = amp - last_amp;
|
||||
if ( delta )
|
||||
{
|
||||
last_amp = amp;
|
||||
synth->offset_inline( time, delta, output );
|
||||
}
|
||||
time += period;
|
||||
}
|
||||
while ( time < end_time );
|
||||
|
||||
this->wave_pos = wave_pos;
|
||||
}
|
||||
delay = time - end_time;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Gb_Noise
|
||||
|
||||
void Gb_Noise::reset()
|
||||
{
|
||||
bits = 1;
|
||||
tap = 14;
|
||||
Gb_Env::reset();
|
||||
}
|
||||
|
||||
Gb_Noise::Gb_Noise() {
|
||||
}
|
||||
|
||||
void Gb_Noise::write_register( int reg, int value )
|
||||
{
|
||||
if ( reg == 1 ) {
|
||||
new_length = length = 64 - (value & 0x3f);
|
||||
}
|
||||
else if ( reg == 2 ) {
|
||||
// based on VBA code, noise is the only exception to the envelope code
|
||||
// while the volume level here is applied when the channel is enabled,
|
||||
// current volume is only affected by writes to this register if volume
|
||||
// is zero and direction is up... (definitely needs verification)
|
||||
int temp = volume;
|
||||
Gb_Env::write_register( reg, value );
|
||||
if ( ( value & 0xF8 ) != 0 ) volume = temp;
|
||||
return;
|
||||
}
|
||||
else if ( reg == 3 ) {
|
||||
tap = 14 - (value & 8);
|
||||
// noise formula and frequency tested against Metroid 2 and Zelda LA
|
||||
int divisor = (value & 7) * 16;
|
||||
if ( !divisor )
|
||||
divisor = 8;
|
||||
period = divisor << (value >> 4);
|
||||
}
|
||||
else if ( reg == 4 && value & trigger ) {
|
||||
bits = ~0u;
|
||||
length = new_length;
|
||||
}
|
||||
|
||||
Gb_Env::write_register( reg, value );
|
||||
}
|
||||
|
||||
#include BLARGG_ENABLE_OPTIMIZER
|
||||
|
||||
void Gb_Noise::run( gb_time_t time, gb_time_t end_time )
|
||||
{
|
||||
if ( !enabled || (!length && length_enabled) || !volume ) {
|
||||
if ( last_amp ) {
|
||||
synth->offset( time, -last_amp, output );
|
||||
last_amp = 0;
|
||||
}
|
||||
delay = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int amp = bits & 1 ? -volume : volume;
|
||||
amp *= global_volume;
|
||||
if ( amp != last_amp ) {
|
||||
synth->offset( time, amp - last_amp, output );
|
||||
last_amp = amp;
|
||||
}
|
||||
|
||||
time += delay;
|
||||
if ( time < end_time )
|
||||
{
|
||||
Blip_Buffer* const output = this->output;
|
||||
// keep parallel resampled time to eliminate multiplication in the loop
|
||||
const blip_resampled_time_t resampled_period =
|
||||
output->resampled_duration( period );
|
||||
blip_resampled_time_t resampled_time = output->resampled_time( time );
|
||||
const unsigned mask = ~(1u << tap);
|
||||
unsigned bits = this->bits;
|
||||
amp *= 2;
|
||||
|
||||
do {
|
||||
unsigned feedback = bits;
|
||||
bits >>= 1;
|
||||
feedback = 1 & (feedback ^ bits);
|
||||
time += period;
|
||||
bits = (feedback << tap) | (bits & mask);
|
||||
// feedback just happens to be true only when the level needs to change
|
||||
// (the previous and current bits are different)
|
||||
if ( feedback ) {
|
||||
amp = -amp;
|
||||
synth->offset_resampled( resampled_time, amp, output );
|
||||
}
|
||||
resampled_time += resampled_period;
|
||||
}
|
||||
while ( time < end_time );
|
||||
|
||||
this->bits = bits;
|
||||
last_amp = amp >> 1;
|
||||
}
|
||||
delay = time - end_time;
|
||||
}
|
||||
}
|
||||
|
||||
100
plugins/papu/gb_apu/Gb_Oscs.h
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
// Private oscillators used by Gb_Apu
|
||||
|
||||
// Gb_Snd_Emu 0.1.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
|
||||
|
||||
#ifndef GB_OSCS_H
|
||||
#define GB_OSCS_H
|
||||
|
||||
#include "Blip_Buffer.h"
|
||||
|
||||
enum { gb_apu_max_vol = 7 };
|
||||
|
||||
struct Gb_Osc {
|
||||
Blip_Buffer* outputs [4]; // NULL, right, left, center
|
||||
Blip_Buffer* output;
|
||||
int output_select;
|
||||
|
||||
int delay;
|
||||
int last_amp;
|
||||
int period;
|
||||
int volume;
|
||||
int global_volume;
|
||||
int frequency;
|
||||
int length;
|
||||
int new_length;
|
||||
bool enabled;
|
||||
bool length_enabled;
|
||||
|
||||
Gb_Osc();
|
||||
|
||||
void clock_length();
|
||||
void reset();
|
||||
virtual void run( gb_time_t begin, gb_time_t end ) = 0;
|
||||
virtual void write_register( int reg, int value );
|
||||
};
|
||||
|
||||
struct Gb_Env : Gb_Osc {
|
||||
int env_period;
|
||||
int env_dir;
|
||||
int env_delay;
|
||||
int new_volume;
|
||||
|
||||
Gb_Env();
|
||||
void reset();
|
||||
void clock_envelope();
|
||||
void write_register( int, int );
|
||||
};
|
||||
|
||||
struct Gb_Square : Gb_Env {
|
||||
int phase;
|
||||
int duty;
|
||||
|
||||
int sweep_period;
|
||||
int sweep_delay;
|
||||
int sweep_shift;
|
||||
int sweep_dir;
|
||||
int sweep_freq;
|
||||
bool has_sweep;
|
||||
|
||||
typedef Blip_Synth<blip_good_quality,15 * gb_apu_max_vol * 2> Synth;
|
||||
const Synth* synth;
|
||||
|
||||
Gb_Square();
|
||||
void reset();
|
||||
void run( gb_time_t, gb_time_t );
|
||||
void write_register( int, int );
|
||||
void clock_sweep();
|
||||
};
|
||||
|
||||
struct Gb_Wave : Gb_Osc {
|
||||
int volume_shift;
|
||||
unsigned wave_pos;
|
||||
enum { wave_size = 32 };
|
||||
bool new_enabled;
|
||||
BOOST::uint8_t wave [wave_size];
|
||||
|
||||
typedef Blip_Synth<blip_med_quality,15 * gb_apu_max_vol * 2> Synth;
|
||||
const Synth* synth;
|
||||
|
||||
Gb_Wave();
|
||||
void reset();
|
||||
void run( gb_time_t, gb_time_t );
|
||||
void write_register( int, int );
|
||||
};
|
||||
|
||||
struct Gb_Noise : Gb_Env {
|
||||
unsigned bits;
|
||||
int tap;
|
||||
|
||||
typedef Blip_Synth<blip_med_quality,15 * gb_apu_max_vol * 2> Synth;
|
||||
const Synth* synth;
|
||||
|
||||
Gb_Noise();
|
||||
void reset();
|
||||
void run( gb_time_t, gb_time_t );
|
||||
void write_register( int, int );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
504
plugins/papu/gb_apu/LGPL.txt
Normal file
@@ -0,0 +1,504 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
215
plugins/papu/gb_apu/Multi_Buffer.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
|
||||
// Blip_Buffer 0.3.4. http://www.slack.net/~ant/libs/
|
||||
|
||||
#include "Multi_Buffer.h"
|
||||
|
||||
/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details. You should have received a copy of the GNU Lesser General
|
||||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include BLARGG_SOURCE_BEGIN
|
||||
|
||||
Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf )
|
||||
{
|
||||
length_ = 0;
|
||||
sample_rate_ = 0;
|
||||
channels_changed_count_ = 1;
|
||||
}
|
||||
|
||||
blargg_err_t Multi_Buffer::set_channel_count( int )
|
||||
{
|
||||
return blargg_success;
|
||||
}
|
||||
|
||||
Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 )
|
||||
{
|
||||
}
|
||||
|
||||
Mono_Buffer::~Mono_Buffer()
|
||||
{
|
||||
}
|
||||
|
||||
blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec )
|
||||
{
|
||||
BLARGG_RETURN_ERR( buf.set_sample_rate( rate, msec ) );
|
||||
return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
|
||||
}
|
||||
|
||||
// Silent_Buffer
|
||||
|
||||
Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse
|
||||
{
|
||||
chan.left = NULL;
|
||||
chan.center = NULL;
|
||||
chan.right = NULL;
|
||||
}
|
||||
|
||||
// Mono_Buffer
|
||||
|
||||
Mono_Buffer::channel_t Mono_Buffer::channel( int index )
|
||||
{
|
||||
channel_t ch;
|
||||
ch.center = &buf;
|
||||
ch.left = &buf;
|
||||
ch.right = &buf;
|
||||
return ch;
|
||||
}
|
||||
|
||||
void Mono_Buffer::end_frame( blip_time_t t, bool )
|
||||
{
|
||||
buf.end_frame( t );
|
||||
}
|
||||
|
||||
// Stereo_Buffer
|
||||
|
||||
Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 )
|
||||
{
|
||||
chan.center = &bufs [0];
|
||||
chan.left = &bufs [1];
|
||||
chan.right = &bufs [2];
|
||||
}
|
||||
|
||||
Stereo_Buffer::~Stereo_Buffer()
|
||||
{
|
||||
}
|
||||
|
||||
blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec )
|
||||
{
|
||||
for ( int i = 0; i < buf_count; i++ )
|
||||
BLARGG_RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) );
|
||||
return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() );
|
||||
}
|
||||
|
||||
void Stereo_Buffer::clock_rate( long rate )
|
||||
{
|
||||
for ( int i = 0; i < buf_count; i++ )
|
||||
bufs [i].clock_rate( rate );
|
||||
}
|
||||
|
||||
void Stereo_Buffer::bass_freq( int bass )
|
||||
{
|
||||
for ( unsigned i = 0; i < buf_count; i++ )
|
||||
bufs [i].bass_freq( bass );
|
||||
}
|
||||
|
||||
void Stereo_Buffer::clear()
|
||||
{
|
||||
stereo_added = false;
|
||||
was_stereo = false;
|
||||
for ( int i = 0; i < buf_count; i++ )
|
||||
bufs [i].clear();
|
||||
}
|
||||
|
||||
void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo )
|
||||
{
|
||||
for ( unsigned i = 0; i < buf_count; i++ )
|
||||
bufs [i].end_frame( clock_count );
|
||||
|
||||
stereo_added |= stereo;
|
||||
}
|
||||
|
||||
long Stereo_Buffer::read_samples( blip_sample_t* out, long count )
|
||||
{
|
||||
require( !(count & 1) ); // count must be even
|
||||
count = (unsigned) count / 2;
|
||||
|
||||
long avail = bufs [0].samples_avail();
|
||||
if ( count > avail )
|
||||
count = avail;
|
||||
if ( count )
|
||||
{
|
||||
if ( stereo_added || was_stereo )
|
||||
{
|
||||
mix_stereo( out, count );
|
||||
|
||||
bufs [0].remove_samples( count );
|
||||
bufs [1].remove_samples( count );
|
||||
bufs [2].remove_samples( count );
|
||||
}
|
||||
else
|
||||
{
|
||||
mix_mono( out, count );
|
||||
|
||||
bufs [0].remove_samples( count );
|
||||
|
||||
bufs [1].remove_silence( count );
|
||||
bufs [2].remove_silence( count );
|
||||
}
|
||||
|
||||
// to do: this might miss opportunities for optimization
|
||||
if ( !bufs [0].samples_avail() ) {
|
||||
was_stereo = stereo_added;
|
||||
stereo_added = false;
|
||||
}
|
||||
}
|
||||
|
||||
return count * 2;
|
||||
}
|
||||
|
||||
#include BLARGG_ENABLE_OPTIMIZER
|
||||
|
||||
void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count )
|
||||
{
|
||||
Blip_Reader left;
|
||||
Blip_Reader right;
|
||||
Blip_Reader center;
|
||||
|
||||
left.begin( bufs [1] );
|
||||
right.begin( bufs [2] );
|
||||
int bass = center.begin( bufs [0] );
|
||||
|
||||
while ( count-- )
|
||||
{
|
||||
int c = center.read();
|
||||
long l = c + left.read();
|
||||
long r = c + right.read();
|
||||
center.next( bass );
|
||||
out [0] = l;
|
||||
out [1] = r;
|
||||
out += 2;
|
||||
|
||||
if ( (BOOST::int16_t) l != l )
|
||||
out [-2] = 0x7FFF - (l >> 24);
|
||||
|
||||
left.next( bass );
|
||||
right.next( bass );
|
||||
|
||||
if ( (BOOST::int16_t) r != r )
|
||||
out [-1] = 0x7FFF - (r >> 24);
|
||||
}
|
||||
|
||||
center.end( bufs [0] );
|
||||
right.end( bufs [2] );
|
||||
left.end( bufs [1] );
|
||||
}
|
||||
|
||||
void Stereo_Buffer::mix_mono( blip_sample_t* out, long count )
|
||||
{
|
||||
Blip_Reader in;
|
||||
int bass = in.begin( bufs [0] );
|
||||
|
||||
while ( count-- )
|
||||
{
|
||||
long s = in.read();
|
||||
in.next( bass );
|
||||
out [0] = s;
|
||||
out [1] = s;
|
||||
out += 2;
|
||||
|
||||
if ( (BOOST::int16_t) s != s ) {
|
||||
s = 0x7FFF - (s >> 24);
|
||||
out [-2] = s;
|
||||
out [-1] = s;
|
||||
}
|
||||
}
|
||||
|
||||
in.end( bufs [0] );
|
||||
}
|
||||
|
||||
174
plugins/papu/gb_apu/Multi_Buffer.h
Normal file
@@ -0,0 +1,174 @@
|
||||
|
||||
// Multi-channel sound buffer interface, and basic mono and stereo buffers
|
||||
|
||||
// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
|
||||
|
||||
#ifndef MULTI_BUFFER_H
|
||||
#define MULTI_BUFFER_H
|
||||
|
||||
#include "Blip_Buffer.h"
|
||||
|
||||
// Interface to one or more Blip_Buffers mapped to one or more channels
|
||||
// consisting of left, center, and right buffers.
|
||||
class Multi_Buffer {
|
||||
public:
|
||||
Multi_Buffer( int samples_per_frame );
|
||||
virtual ~Multi_Buffer() { }
|
||||
|
||||
// Set the number of channels available
|
||||
virtual blargg_err_t set_channel_count( int );
|
||||
|
||||
// Get indexed channel, from 0 to channel count - 1
|
||||
struct channel_t {
|
||||
Blip_Buffer* center;
|
||||
Blip_Buffer* left;
|
||||
Blip_Buffer* right;
|
||||
};
|
||||
virtual channel_t channel( int index ) = 0;
|
||||
|
||||
// See Blip_Buffer.h
|
||||
virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0;
|
||||
virtual void clock_rate( long ) = 0;
|
||||
virtual void bass_freq( int ) = 0;
|
||||
virtual void clear() = 0;
|
||||
long sample_rate() const;
|
||||
|
||||
// Length of buffer, in milliseconds
|
||||
int length() const;
|
||||
|
||||
// See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo'
|
||||
// if nothing was added to the left and right buffers of any channel for
|
||||
// this time frame.
|
||||
virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0;
|
||||
|
||||
// Number of samples per output frame (1 = mono, 2 = stereo)
|
||||
int samples_per_frame() const;
|
||||
|
||||
// Count of changes to channel configuration. Incremented whenever
|
||||
// a change is made to any of the Blip_Buffers for any channel.
|
||||
unsigned channels_changed_count() { return channels_changed_count_; }
|
||||
|
||||
// See Blip_Buffer.h
|
||||
virtual long read_samples( blip_sample_t*, long ) = 0;
|
||||
virtual long samples_avail() const = 0;
|
||||
|
||||
protected:
|
||||
void channels_changed() { channels_changed_count_++; }
|
||||
private:
|
||||
// noncopyable
|
||||
Multi_Buffer( const Multi_Buffer& );
|
||||
Multi_Buffer& operator = ( const Multi_Buffer& );
|
||||
|
||||
unsigned channels_changed_count_;
|
||||
long sample_rate_;
|
||||
int length_;
|
||||
int const samples_per_frame_;
|
||||
};
|
||||
|
||||
// Uses a single buffer and outputs mono samples.
|
||||
class Mono_Buffer : public Multi_Buffer {
|
||||
Blip_Buffer buf;
|
||||
public:
|
||||
Mono_Buffer();
|
||||
~Mono_Buffer();
|
||||
|
||||
// Buffer used for all channels
|
||||
Blip_Buffer* center() { return &buf; }
|
||||
|
||||
// See Multi_Buffer
|
||||
blargg_err_t set_sample_rate( long rate, int msec = blip_default_length );
|
||||
void clock_rate( long );
|
||||
void bass_freq( int );
|
||||
void clear();
|
||||
channel_t channel( int );
|
||||
void end_frame( blip_time_t, bool unused = true );
|
||||
long samples_avail() const;
|
||||
long read_samples( blip_sample_t*, long );
|
||||
};
|
||||
|
||||
// Uses three buffers (one for center) and outputs stereo sample pairs.
|
||||
class Stereo_Buffer : public Multi_Buffer {
|
||||
public:
|
||||
Stereo_Buffer();
|
||||
~Stereo_Buffer();
|
||||
|
||||
// Buffers used for all channels
|
||||
Blip_Buffer* center() { return &bufs [0]; }
|
||||
Blip_Buffer* left() { return &bufs [1]; }
|
||||
Blip_Buffer* right() { return &bufs [2]; }
|
||||
|
||||
// See Multi_Buffer
|
||||
blargg_err_t set_sample_rate( long, int msec = blip_default_length );
|
||||
void clock_rate( long );
|
||||
void bass_freq( int );
|
||||
void clear();
|
||||
channel_t channel( int index );
|
||||
void end_frame( blip_time_t, bool added_stereo = true );
|
||||
|
||||
long samples_avail() const;
|
||||
long read_samples( blip_sample_t*, long );
|
||||
|
||||
private:
|
||||
enum { buf_count = 3 };
|
||||
Blip_Buffer bufs [buf_count];
|
||||
channel_t chan;
|
||||
bool stereo_added;
|
||||
bool was_stereo;
|
||||
|
||||
void mix_stereo( blip_sample_t*, long );
|
||||
void mix_mono( blip_sample_t*, long );
|
||||
};
|
||||
|
||||
// Silent_Buffer generates no samples, useful where no sound is wanted
|
||||
class Silent_Buffer : public Multi_Buffer {
|
||||
channel_t chan;
|
||||
public:
|
||||
Silent_Buffer();
|
||||
|
||||
blargg_err_t set_sample_rate( long rate, int msec = blip_default_length );
|
||||
void clock_rate( long ) { }
|
||||
void bass_freq( int ) { }
|
||||
void clear() { }
|
||||
channel_t channel( int ) { return chan; }
|
||||
void end_frame( blip_time_t, bool unused = true ) { }
|
||||
long samples_avail() const { return 0; }
|
||||
long read_samples( blip_sample_t*, long ) { return 0; }
|
||||
};
|
||||
|
||||
|
||||
// End of public interface
|
||||
|
||||
inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec )
|
||||
{
|
||||
return Multi_Buffer::set_sample_rate( rate, msec );
|
||||
}
|
||||
|
||||
inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec )
|
||||
{
|
||||
sample_rate_ = rate;
|
||||
length_ = msec;
|
||||
return blargg_success;
|
||||
}
|
||||
|
||||
inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; }
|
||||
|
||||
inline long Stereo_Buffer::samples_avail() const { return bufs [0].samples_avail() * 2; }
|
||||
|
||||
inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int index ) { return chan; }
|
||||
|
||||
inline long Multi_Buffer::sample_rate() const { return sample_rate_; }
|
||||
|
||||
inline int Multi_Buffer::length() const { return length_; }
|
||||
|
||||
inline void Mono_Buffer::clock_rate( long rate ) { buf.clock_rate( rate ); }
|
||||
|
||||
inline void Mono_Buffer::clear() { buf.clear(); }
|
||||
|
||||
inline void Mono_Buffer::bass_freq( int freq ) { buf.bass_freq( freq ); }
|
||||
|
||||
inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); }
|
||||
|
||||
inline long Mono_Buffer::samples_avail() const { return buf.samples_avail(); }
|
||||
|
||||
#endif
|
||||
|
||||
178
plugins/papu/gb_apu/blargg_common.h
Normal file
@@ -0,0 +1,178 @@
|
||||
|
||||
// Sets up common environment for Shay Green's libraries.
|
||||
//
|
||||
// Don't modify this file directly; #define HAVE_CONFIG_H and put your
|
||||
// configuration into "config.h".
|
||||
|
||||
// Copyright (C) 2004-2005 Shay Green.
|
||||
|
||||
#ifndef BLARGG_COMMON_H
|
||||
#define BLARGG_COMMON_H
|
||||
|
||||
// Allow prefix configuration file *which can re-include blargg_common.h*
|
||||
// (probably indirectly).
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#undef BLARGG_COMMON_H
|
||||
#include "config.h"
|
||||
#define BLARGG_COMMON_H
|
||||
#endif
|
||||
|
||||
// Source files use #include BLARGG_ENABLE_OPTIMIZER before performance-critical code
|
||||
#ifndef BLARGG_ENABLE_OPTIMIZER
|
||||
#define BLARGG_ENABLE_OPTIMIZER "blargg_common.h"
|
||||
#endif
|
||||
|
||||
// Source files have #include BLARGG_SOURCE_BEGIN at the beginning
|
||||
#ifndef BLARGG_SOURCE_BEGIN
|
||||
#define BLARGG_SOURCE_BEGIN "blargg_source.h"
|
||||
#endif
|
||||
|
||||
// Determine compiler's language support
|
||||
|
||||
#if defined (__MWERKS__)
|
||||
// Metrowerks CodeWarrior
|
||||
#define BLARGG_COMPILER_HAS_NAMESPACE 1
|
||||
#if !__option(bool)
|
||||
#define BLARGG_COMPILER_HAS_BOOL 0
|
||||
#endif
|
||||
|
||||
#elif defined (_MSC_VER)
|
||||
// Microsoft Visual C++
|
||||
#if _MSC_VER < 1100
|
||||
#define BLARGG_COMPILER_HAS_BOOL 0
|
||||
#endif
|
||||
|
||||
#elif defined (__GNUC__)
|
||||
// GNU C++
|
||||
#define BLARGG_COMPILER_HAS_NAMESPACE 1
|
||||
#define BLARGG_COMPILER_HAS_BOOL 1
|
||||
|
||||
#elif defined (__MINGW32__)
|
||||
// Mingw?
|
||||
#define BLARGG_COMPILER_HAS_BOOL 1
|
||||
|
||||
#elif __cplusplus < 199711
|
||||
// Pre-ISO C++ compiler
|
||||
#define BLARGG_COMPILER_HAS_BOOL 0
|
||||
#define STATIC_CAST( type ) (type)
|
||||
|
||||
#endif
|
||||
|
||||
// STATIC_CAST(T) (expr) -> static_cast< T > (expr)
|
||||
#ifndef STATIC_CAST
|
||||
#define STATIC_CAST( type ) static_cast< type >
|
||||
#endif
|
||||
|
||||
// Set up boost
|
||||
#include "boost/config.hpp"
|
||||
#ifndef BOOST_MINIMAL
|
||||
#define BOOST boost
|
||||
#ifndef BLARGG_COMPILER_HAS_NAMESPACE
|
||||
#define BLARGG_COMPILER_HAS_NAMESPACE 1
|
||||
#endif
|
||||
#ifndef BLARGG_COMPILER_HAS_BOOL
|
||||
#define BLARGG_COMPILER_HAS_BOOL 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Bool support
|
||||
#ifndef BLARGG_COMPILER_HAS_BOOL
|
||||
#define BLARGG_COMPILER_HAS_BOOL 1
|
||||
#elif !BLARGG_COMPILER_HAS_BOOL
|
||||
typedef int bool;
|
||||
const bool true = 1;
|
||||
const bool false = 0;
|
||||
#endif
|
||||
|
||||
// Set up namespace support
|
||||
|
||||
#ifndef BLARGG_COMPILER_HAS_NAMESPACE
|
||||
#define BLARGG_COMPILER_HAS_NAMESPACE 0
|
||||
#endif
|
||||
|
||||
#ifndef BLARGG_USE_NAMESPACE
|
||||
#define BLARGG_USE_NAMESPACE BLARGG_COMPILER_HAS_NAMESPACE
|
||||
#endif
|
||||
|
||||
#ifndef BOOST
|
||||
#if BLARGG_USE_NAMESPACE
|
||||
#define BOOST boost
|
||||
#else
|
||||
#define BOOST
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#undef BLARGG_BEGIN_NAMESPACE
|
||||
#undef BLARGG_END_NAMESPACE
|
||||
#if BLARGG_USE_NAMESPACE
|
||||
#define BLARGG_BEGIN_NAMESPACE( name ) namespace name {
|
||||
#define BLARGG_END_NAMESPACE }
|
||||
#else
|
||||
#define BLARGG_BEGIN_NAMESPACE( name )
|
||||
#define BLARGG_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
#if BLARGG_USE_NAMESPACE
|
||||
#define STD std
|
||||
#else
|
||||
#define STD
|
||||
#endif
|
||||
|
||||
// BOOST::uint8_t, BOOST::int16_t, etc.
|
||||
#include "boost/cstdint.hpp"
|
||||
|
||||
// BOOST_STATIC_ASSERT( expr )
|
||||
#include "boost/static_assert.hpp"
|
||||
|
||||
// Common standard headers
|
||||
#if BLARGG_COMPILER_HAS_NAMESPACE
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
// blargg_err_t (NULL on success, otherwise error string)
|
||||
typedef const char* blargg_err_t;
|
||||
const blargg_err_t blargg_success = 0;
|
||||
|
||||
// BLARGG_NEW is used in place of 'new' to create objects. By default,
|
||||
// plain new is used.
|
||||
#ifndef BLARGG_NEW
|
||||
#define BLARGG_NEW new
|
||||
#endif
|
||||
|
||||
// BLARGG_BIG_ENDIAN and BLARGG_LITTLE_ENDIAN
|
||||
// Only needed if modules are used which must know byte order.
|
||||
#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN)
|
||||
#if defined (__powerc) || defined (macintosh)
|
||||
#define BLARGG_BIG_ENDIAN 1
|
||||
|
||||
#elif defined (_MSC_VER) && defined (_M_IX86)
|
||||
#define BLARGG_LITTLE_ENDIAN 1
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// BLARGG_NONPORTABLE (allow use of nonportable optimizations/features)
|
||||
#ifndef BLARGG_NONPORTABLE
|
||||
#define BLARGG_NONPORTABLE 0
|
||||
#endif
|
||||
#ifdef BLARGG_MOST_PORTABLE
|
||||
#error "BLARGG_MOST_PORTABLE has been removed; use BLARGG_NONPORTABLE."
|
||||
#endif
|
||||
|
||||
// BLARGG_CPU_*
|
||||
#if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86)
|
||||
#if defined (__powerc)
|
||||
#define BLARGG_CPU_POWERPC 1
|
||||
|
||||
#elif defined (_MSC_VER) && defined (_M_IX86)
|
||||
#define BLARGG_CPU_X86 1
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
66
plugins/papu/gb_apu/blargg_source.h
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
// By default, #included at beginning of library source files
|
||||
|
||||
// Copyright (C) 2005 Shay Green.
|
||||
|
||||
#ifndef BLARGG_SOURCE_H
|
||||
#define BLARGG_SOURCE_H
|
||||
|
||||
// If debugging is enabled, abort program if expr is false. Meant for checking
|
||||
// internal state and consistency. A failed assertion indicates a bug in the module.
|
||||
// void assert( bool expr );
|
||||
#include <assert.h>
|
||||
|
||||
// If debugging is enabled and expr is false, abort program. Meant for checking
|
||||
// caller-supplied parameters and operations that are outside the control of the
|
||||
// module. A failed requirement indicates a bug outside the module.
|
||||
// void require( bool expr );
|
||||
#undef require
|
||||
#define require( expr ) assert(( "unmet requirement", expr ))
|
||||
|
||||
// Like printf() except output goes to debug log file. Might be defined to do
|
||||
// nothing (not even evaluate its arguments).
|
||||
// void dprintf( const char* format, ... );
|
||||
#undef dprintf
|
||||
#define dprintf (1) ? ((void) 0) : (void)
|
||||
|
||||
// If enabled, evaluate expr and if false, make debug log entry with source file
|
||||
// and line. Meant for finding situations that should be examined further, but that
|
||||
// don't indicate a problem. In all cases, execution continues normally.
|
||||
#undef check
|
||||
#define check( expr ) ((void) 0)
|
||||
|
||||
// If expr returns non-NULL error string, return it from current function, otherwise continue.
|
||||
#define BLARGG_RETURN_ERR( expr ) do { \
|
||||
blargg_err_t blargg_return_err_ = (expr); \
|
||||
if ( blargg_return_err_ ) return blargg_return_err_; \
|
||||
} while ( 0 )
|
||||
|
||||
// If ptr is NULL, return out of memory error string.
|
||||
#define BLARGG_CHECK_ALLOC( ptr ) do { if ( !(ptr) ) return "Out of memory"; } while ( 0 )
|
||||
|
||||
// Avoid any macros which evaluate their arguments multiple times
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
// using const references generates crappy code, and I am currenly only using these
|
||||
// for built-in types, so they take arguments by value
|
||||
|
||||
template<class T>
|
||||
inline T min( T x, T y )
|
||||
{
|
||||
if ( x < y )
|
||||
return x;
|
||||
return y;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T max( T x, T y )
|
||||
{
|
||||
if ( x < y )
|
||||
return y;
|
||||
return x;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
13
plugins/papu/gb_apu/boost/config.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
// Boost substitute. For full boost library see http://boost.org
|
||||
|
||||
#ifndef BOOST_CONFIG_HPP
|
||||
#define BOOST_CONFIG_HPP
|
||||
|
||||
#define BOOST_MINIMAL 1
|
||||
|
||||
#define BLARGG_BEGIN_NAMESPACE( name )
|
||||
#define BLARGG_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
42
plugins/papu/gb_apu/boost/cstdint.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
// Boost substitute. For full boost library see http://boost.org
|
||||
|
||||
#ifndef BOOST_CSTDINT_HPP
|
||||
#define BOOST_CSTDINT_HPP
|
||||
|
||||
#if BLARGG_USE_NAMESPACE
|
||||
#include <climits>
|
||||
#else
|
||||
#include <limits.h>
|
||||
#endif
|
||||
|
||||
BLARGG_BEGIN_NAMESPACE( boost )
|
||||
|
||||
#if UCHAR_MAX != 0xFF || SCHAR_MAX != 0x7F
|
||||
# error "No suitable 8-bit type available"
|
||||
#endif
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef signed char int8_t;
|
||||
|
||||
#if USHRT_MAX != 0xFFFF
|
||||
# error "No suitable 16-bit type available"
|
||||
#endif
|
||||
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
|
||||
#if ULONG_MAX == 0xFFFFFFFF
|
||||
typedef long int32_t;
|
||||
typedef unsigned long uint32_t;
|
||||
#elif UINT_MAX == 0xFFFFFFFF
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
# error "No suitable 32-bit type available"
|
||||
#endif
|
||||
|
||||
BLARGG_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
22
plugins/papu/gb_apu/boost/static_assert.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
// Boost substitute. For full boost library see http://boost.org
|
||||
|
||||
#ifndef BOOST_STATIC_ASSERT_HPP
|
||||
#define BOOST_STATIC_ASSERT_HPP
|
||||
|
||||
#if defined (_MSC_VER) && _MSC_VER <= 1200
|
||||
// MSVC6 can't handle the ##line concatenation
|
||||
#define BOOST_STATIC_ASSERT( expr ) struct { int n [1 / ((expr) ? 1 : 0)]; }
|
||||
|
||||
#else
|
||||
#define BOOST_STATIC_ASSERT3( expr, line ) \
|
||||
typedef int boost_static_assert_##line [1 / ((expr) ? 1 : 0)]
|
||||
|
||||
#define BOOST_STATIC_ASSERT2( expr, line ) BOOST_STATIC_ASSERT3( expr, line )
|
||||
|
||||
#define BOOST_STATIC_ASSERT( expr ) BOOST_STATIC_ASSERT2( expr, __LINE__ )
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
BIN
plugins/papu/logo.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
724
plugins/papu/papu_instrument.cpp
Normal file
@@ -0,0 +1,724 @@
|
||||
/*
|
||||
* papu_instrument.cpp - GameBoy papu based instrument
|
||||
*
|
||||
* Copyright (c) 2008 Attila Herman <attila589/at/gmail.com>
|
||||
* Csaba Hruska <csaba.hruska/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <Qt/QtXml>
|
||||
#include <QtGui/QPainter>
|
||||
#include "Basic_Gb_Apu.h"
|
||||
|
||||
#include "papu_instrument.h"
|
||||
#include "instrument_track.h"
|
||||
#include "knob.h"
|
||||
#include "note_play_handle.h"
|
||||
#include "pixmap_button.h"
|
||||
#include "tooltip.h"
|
||||
#include "graph.h"
|
||||
|
||||
#undef SINGLE_SOURCE_COMPILE
|
||||
#include "embed.cpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
plugin::descriptor PLUGIN_EXPORT papu_plugin_descriptor =
|
||||
{
|
||||
STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ),
|
||||
"PAPU",
|
||||
QT_TRANSLATE_NOOP( "pluginBrowser", "Emulation of GameBoy APU" ),
|
||||
|
||||
"Attila Herman <attila589/at/gmail.com>"
|
||||
"Csaba Hruska <csaba.hruska/at/gmail.com>",
|
||||
0x0100,
|
||||
plugin::Instrument,
|
||||
new pluginPixmapLoader( "logo" ),
|
||||
NULL
|
||||
} ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
papuInstrument::papuInstrument( instrumentTrack * _instrument_track ) :
|
||||
instrument( _instrument_track, &papu_plugin_descriptor ),
|
||||
|
||||
m_ch1SweepTimeModel( 4.0f, 0.0f, 7.0f, 1.0f, this, tr( "Sweep time" ) ),
|
||||
m_ch1SweepDirModel( false, this, tr( "Sweep direction" ) ),
|
||||
m_ch1SweepRtShiftModel( 4.0f, 0.0f, 7.0f, 1.0f, this,
|
||||
tr( "Sweep RtShift amount" ) ),
|
||||
m_ch1WavePatternDutyModel( 2.0f, 0.0f, 3.0f, 1.0f, this,
|
||||
tr( "Wave Pattern Duty" ) ),
|
||||
m_ch1VolumeModel( 15.0f, 0.0f, 15.0f, 1.0f, this,
|
||||
tr( "Channel 1 volume" ) ),
|
||||
m_ch1VolSweepDirModel( false, this,
|
||||
tr( "Volume sweep direction" ) ),
|
||||
m_ch1SweepStepLengthModel( 0.0f, 0.0f, 7.0f, 1.0f, this,
|
||||
tr( "Length of each step in sweep" ) ),
|
||||
|
||||
m_ch2WavePatternDutyModel( 2.0f, 0.0f, 3.0f, 1.0f, this,
|
||||
tr( "Wave Pattern Duty" ) ),
|
||||
m_ch2VolumeModel( 15.0f, 0.0f, 15.0f, 1.0f, this,
|
||||
tr( "Channel 2 volume" ) ),
|
||||
m_ch2VolSweepDirModel( false, this,
|
||||
tr( "Volume sweep direction" ) ),
|
||||
m_ch2SweepStepLengthModel( 0.0f, 0.0f, 7.0f, 1.0f, this,
|
||||
tr( "Length of each step in sweep" ) ),
|
||||
|
||||
//m_ch3OnModel( true, this, tr( "Channel 3 Master on/off" ) ),
|
||||
m_ch3VolumeModel( 3.0f, 0.0f, 3.0f, 1.0f, this,
|
||||
tr( "Channel 3 volume" ) ),
|
||||
|
||||
m_ch4VolumeModel( 15.0f, 0.0f, 15.0f, 1.0f, this,
|
||||
tr( "Channel 4 volume" ) ),
|
||||
m_ch4VolSweepDirModel( false, this,
|
||||
tr( "Volume sweep direction" ) ),
|
||||
m_ch4SweepStepLengthModel( 0.0f, 0.0f, 7.0f, 1.0f, this,
|
||||
tr( "Length of each step in sweep" ) ),
|
||||
m_ch4ShiftRegWidthModel( false, this,
|
||||
tr( "Shift Register width (0: 15 bits; 1: 7 bits)" ) ),
|
||||
|
||||
m_so1VolumeModel( 7.0f, 0.0f, 7.0f, 1.0f, this, tr( "Right Output level") ),
|
||||
m_so2VolumeModel( 7.0f, 0.0f, 7.0f, 1.0f, this, tr( "Left Output level" ) ),
|
||||
m_ch1So1Model( true, this, tr( "Channel 1 to SO2 (Left)" ) ),
|
||||
m_ch2So1Model( true, this, tr( "Channel 2 to SO2 (Left)" ) ),
|
||||
m_ch3So1Model( true, this, tr( "Channel 3 to SO2 (Left)" ) ),
|
||||
m_ch4So1Model( true, this, tr( "Channel 4 to SO2 (Left)" ) ),
|
||||
m_ch1So2Model( true, this, tr( "Channel 1 to SO1 (Right)" ) ),
|
||||
m_ch2So2Model( true, this, tr( "Channel 2 to SO1 (Right)" ) ),
|
||||
m_ch3So2Model( true, this, tr( "Channel 3 to SO1 (Right)" ) ),
|
||||
m_ch4So2Model( true, this, tr( "Channel 4 to SO1 (Right)" ) ),
|
||||
m_trebleModel( -20.0f, -100.0f, 200.0f, 1.0f, this, tr( "Treble" ) ),
|
||||
m_bassModel( 461.0f, -1.0f, 600.0f, 1.0f, this, tr( "Bass" ) ),
|
||||
|
||||
m_graphModel( 0, 15, 32, this, FALSE, 1 )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
papuInstrument::~papuInstrument()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void papuInstrument::saveSettings( QDomDocument & _doc,
|
||||
QDomElement & _this )
|
||||
{
|
||||
m_ch1SweepTimeModel.saveSettings( _doc, _this, "st" );
|
||||
m_ch1SweepDirModel.saveSettings( _doc, _this, "sd" );
|
||||
m_ch1SweepRtShiftModel.saveSettings( _doc, _this, "srs" );
|
||||
m_ch1WavePatternDutyModel.saveSettings( _doc, _this, "ch1wpd" );
|
||||
m_ch1VolumeModel.saveSettings( _doc, _this, "ch1vol" );
|
||||
m_ch1VolSweepDirModel.saveSettings( _doc, _this, "ch1vsd" );
|
||||
m_ch1SweepStepLengthModel.saveSettings( _doc, _this, "ch1ssl" );
|
||||
|
||||
m_ch2WavePatternDutyModel.saveSettings( _doc, _this, "ch2wpd" );
|
||||
m_ch2VolumeModel.saveSettings( _doc, _this, "ch2vol" );
|
||||
m_ch2VolSweepDirModel.saveSettings( _doc, _this, "ch2vsd" );
|
||||
m_ch2SweepStepLengthModel.saveSettings( _doc, _this, "ch2ssl" );
|
||||
|
||||
//m_ch3OnModel.saveSettings( _doc, _this, "ch3on" );
|
||||
m_ch3VolumeModel.saveSettings( _doc, _this, "ch3vol" );
|
||||
|
||||
m_ch4VolumeModel.saveSettings( _doc, _this, "ch4vol" );
|
||||
m_ch4VolSweepDirModel.saveSettings( _doc, _this, "ch4vsd" );
|
||||
m_ch4SweepStepLengthModel.saveSettings( _doc, _this, "ch4ssl" );
|
||||
m_ch4ShiftRegWidthModel.saveSettings( _doc, _this, "srw" );
|
||||
|
||||
m_so1VolumeModel.saveSettings( _doc, _this, "so1vol" );
|
||||
m_so2VolumeModel.saveSettings( _doc, _this, "so2vol" );
|
||||
m_ch1So1Model.saveSettings( _doc, _this, "ch1so2" );
|
||||
m_ch2So1Model.saveSettings( _doc, _this, "ch2so2" );
|
||||
m_ch3So1Model.saveSettings( _doc, _this, "ch3so2" );
|
||||
m_ch4So1Model.saveSettings( _doc, _this, "ch4so2" );
|
||||
m_ch1So2Model.saveSettings( _doc, _this, "ch1so1" );
|
||||
m_ch2So2Model.saveSettings( _doc, _this, "ch2so1" );
|
||||
m_ch3So2Model.saveSettings( _doc, _this, "ch3so1" );
|
||||
m_ch4So2Model.saveSettings( _doc, _this, "ch4so1" );
|
||||
m_trebleModel.saveSettings( _doc, _this, "Treble" );
|
||||
m_bassModel.saveSettings( _doc, _this, "Bass" );
|
||||
|
||||
QString sampleString;
|
||||
base64::encode( (const char *)m_graphModel.samples(),
|
||||
m_graphModel.length() * sizeof(float), sampleString );
|
||||
_this.setAttribute( "sampleShape", sampleString );
|
||||
}
|
||||
|
||||
void papuInstrument::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
m_ch1SweepTimeModel.loadSettings( _this, "st" );
|
||||
m_ch1SweepDirModel.loadSettings( _this, "sd" );
|
||||
m_ch1SweepRtShiftModel.loadSettings( _this, "srs" );
|
||||
m_ch1WavePatternDutyModel.loadSettings( _this, "ch1wpd" );
|
||||
m_ch1VolumeModel.loadSettings( _this, "ch1vol" );
|
||||
m_ch1VolSweepDirModel.loadSettings( _this, "ch1vsd" );
|
||||
m_ch1SweepStepLengthModel.loadSettings( _this, "ch1ssl" );
|
||||
|
||||
m_ch2WavePatternDutyModel.loadSettings( _this, "ch2wpd" );
|
||||
m_ch2VolumeModel.loadSettings( _this, "ch2vol" );
|
||||
m_ch2VolSweepDirModel.loadSettings( _this, "ch2vsd" );
|
||||
m_ch2SweepStepLengthModel.loadSettings( _this, "ch2ssl" );
|
||||
|
||||
//m_ch3OnModel.loadSettings( _this, "ch3on" );
|
||||
m_ch3VolumeModel.loadSettings( _this, "ch3vol" );
|
||||
|
||||
m_ch4VolumeModel.loadSettings( _this, "ch4vol" );
|
||||
m_ch4VolSweepDirModel.loadSettings( _this, "ch4vsd" );
|
||||
m_ch4SweepStepLengthModel.loadSettings( _this, "ch4ssl" );
|
||||
m_ch4ShiftRegWidthModel.loadSettings( _this, "srw" );
|
||||
|
||||
m_so1VolumeModel.loadSettings( _this, "so1vol" );
|
||||
m_so2VolumeModel.loadSettings( _this, "so2vol" );
|
||||
m_ch1So1Model.loadSettings( _this, "ch1so2" );
|
||||
m_ch2So1Model.loadSettings( _this, "ch2so2" );
|
||||
m_ch3So1Model.loadSettings( _this, "ch3so2" );
|
||||
m_ch4So1Model.loadSettings( _this, "ch4so2" );
|
||||
m_ch1So2Model.loadSettings( _this, "ch1so1" );
|
||||
m_ch2So2Model.loadSettings( _this, "ch2so1" );
|
||||
m_ch3So2Model.loadSettings( _this, "ch3so1" );
|
||||
m_ch4So2Model.loadSettings( _this, "ch4so1" );
|
||||
m_trebleModel.loadSettings( _this, "Treble" );
|
||||
m_bassModel.loadSettings( _this, "Bass" );
|
||||
|
||||
int size = 0;
|
||||
char * dst = 0;
|
||||
base64::decode( _this.attribute( "sampleShape"), &dst, &size );
|
||||
m_graphModel.setSamples( (float*) dst );
|
||||
}
|
||||
|
||||
QString papuInstrument::nodeName( void ) const
|
||||
{
|
||||
return( papu_plugin_descriptor.name );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*f_cnt_t papuInstrument::desiredReleaseFrames( void ) const
|
||||
{
|
||||
const float samplerate = engine::getMixer()->processingSampleRate();
|
||||
int maxrel = 0;
|
||||
for( int i = 0 ; i < 3 ; ++i )
|
||||
{
|
||||
if( maxrel < m_voice[i]->m_releaseModel.value() )
|
||||
maxrel = m_voice[i]->m_releaseModel.value();
|
||||
}
|
||||
|
||||
return f_cnt_t( float(relTime[maxrel])*samplerate/1000.0 );
|
||||
}*/
|
||||
|
||||
f_cnt_t papuInstrument::desiredReleaseFrames( void ) const
|
||||
{
|
||||
return f_cnt_t( 1000 );
|
||||
}
|
||||
|
||||
void papuInstrument::playNote( notePlayHandle * _n, bool,
|
||||
sampleFrame * _working_buffer )
|
||||
{
|
||||
const f_cnt_t tfp = _n->totalFramesPlayed();
|
||||
const int samplerate = engine::getMixer()->processingSampleRate();
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
|
||||
int data = 0;
|
||||
int freq = _n->frequency();
|
||||
|
||||
if ( tfp == 0 )
|
||||
{
|
||||
Basic_Gb_Apu *papu = new Basic_Gb_Apu();
|
||||
papu->set_sample_rate( samplerate );
|
||||
|
||||
// Master sound circuitry power control
|
||||
papu->write_register( 0xff26, 0x80 );
|
||||
|
||||
data = m_ch1VolumeModel.value();
|
||||
data = data<<1;
|
||||
data += m_ch1VolSweepDirModel.value();
|
||||
data = data<<3;
|
||||
data += m_ch1SweepStepLengthModel.value();
|
||||
papu->write_register( 0xff12, data );
|
||||
|
||||
data = m_ch2VolumeModel.value();
|
||||
data = data<<1;
|
||||
data += m_ch2VolSweepDirModel.value();
|
||||
data = data<<3;
|
||||
data += m_ch2SweepStepLengthModel.value();
|
||||
papu->write_register( 0xff17, data );
|
||||
|
||||
//channel 4 - noise
|
||||
data = m_ch4VolumeModel.value();
|
||||
data = data<<1;
|
||||
data += m_ch4VolSweepDirModel.value();
|
||||
data = data<<3;
|
||||
data += m_ch4SweepStepLengthModel.value();
|
||||
papu->write_register( 0xff21, data );
|
||||
|
||||
//channel 4 init
|
||||
papu->write_register( 0xff23, 128 );
|
||||
|
||||
_n->m_pluginData = papu;
|
||||
}
|
||||
|
||||
Basic_Gb_Apu *papu = static_cast<Basic_Gb_Apu *>( _n->m_pluginData );
|
||||
|
||||
papu->treble_eq( m_trebleModel.value() );
|
||||
papu->bass_freq( m_bassModel.value() );
|
||||
|
||||
//channel 1 - square
|
||||
data = m_ch1SweepTimeModel.value();
|
||||
data = data<<1;
|
||||
data += m_ch1SweepDirModel.value();
|
||||
data = data << 3;
|
||||
data += m_ch1SweepRtShiftModel.value();
|
||||
papu->write_register( 0xff10, data );
|
||||
|
||||
data = m_ch1WavePatternDutyModel.value();
|
||||
data = data<<6;
|
||||
papu->write_register( 0xff11, data );
|
||||
|
||||
|
||||
//channel 2 - square
|
||||
data = m_ch2WavePatternDutyModel.value();
|
||||
data = data<<6;
|
||||
papu->write_register( 0xff16, data );
|
||||
|
||||
|
||||
//channel 3 - wave
|
||||
//data = m_ch3OnModel.value()?128:0;
|
||||
data = 128;
|
||||
papu->write_register( 0xff1a, data );
|
||||
|
||||
int ch3voldata[4] = { 0, 3, 2, 1 };
|
||||
data = ch3voldata[(int)m_ch3VolumeModel.value()];
|
||||
data = data<<5;
|
||||
papu->write_register( 0xff1c, data );
|
||||
|
||||
|
||||
//controls
|
||||
data = m_so1VolumeModel.value();
|
||||
data = data<<4;
|
||||
data += m_so2VolumeModel.value();
|
||||
papu->write_register( 0xff24, data );
|
||||
|
||||
data = m_ch4So2Model.value()?128:0;
|
||||
data += m_ch3So2Model.value()?64:0;
|
||||
data += m_ch2So2Model.value()?32:0;
|
||||
data += m_ch1So2Model.value()?16:0;
|
||||
data += m_ch4So1Model.value()?8:0;
|
||||
data += m_ch3So1Model.value()?4:0;
|
||||
data += m_ch2So1Model.value()?2:0;
|
||||
data += m_ch1So1Model.value()?1:0;
|
||||
papu->write_register( 0xff25, data );
|
||||
|
||||
const float * wpm = m_graphModel.samples();
|
||||
|
||||
for( char i=0; i<16; i++ )
|
||||
{
|
||||
data = (int)floor(wpm[i*2]) << 4;
|
||||
data += (int)floor(wpm[i*2+1]);
|
||||
papu->write_register( 0xff30 + i, data );
|
||||
}
|
||||
|
||||
if( ( freq >= 65 ) && ( freq <=4000 ) )
|
||||
{
|
||||
int initflag = (tfp==0)?128:0;
|
||||
// Hz = 4194304 / ( ( 2048 - ( 11-bit-freq ) ) << 5 )
|
||||
data = 2048 - ( ( 4194304 / freq )>>5 );
|
||||
if( tfp==0 )
|
||||
{
|
||||
papu->write_register( 0xff13, data & 0xff );
|
||||
papu->write_register( 0xff14, (data>>8) | initflag );
|
||||
}
|
||||
papu->write_register( 0xff18, data & 0xff );
|
||||
papu->write_register( 0xff19, (data>>8) | initflag );
|
||||
papu->write_register( 0xff1d, data & 0xff );
|
||||
papu->write_register( 0xff1e, (data>>8) | initflag );
|
||||
}
|
||||
|
||||
if( tfp == 0 )
|
||||
{
|
||||
//PRNG Frequency = (1048576 Hz / (ratio + 1)) / 2 ^ (shiftclockfreq + 1)
|
||||
char sopt=0;
|
||||
char ropt=1;
|
||||
float fopt = 524288.0 / ( ropt * pow( 2, sopt+1 ) );
|
||||
float f;
|
||||
for ( char s=0; s<16; s++ )
|
||||
for ( char r=0; r<8; r++ ) {
|
||||
f = 524288.0 / ( r * pow( 2, s+1 ) );
|
||||
if( fabs( freq-fopt ) > fabs( freq-f ) ) {
|
||||
fopt = f;
|
||||
ropt = r;
|
||||
sopt = s;
|
||||
}
|
||||
}
|
||||
data = sopt;
|
||||
data = data << 1;
|
||||
data += m_ch4ShiftRegWidthModel.value();
|
||||
data = data << 3;
|
||||
data += ropt;
|
||||
papu->write_register( 0xff22, data );
|
||||
}
|
||||
|
||||
int const buf_size = 2048;
|
||||
int framesleft = frames;
|
||||
int datalen = 0;
|
||||
static blip_sample_t buf [buf_size*2];
|
||||
while( framesleft > 0 )
|
||||
{
|
||||
int avail = papu->samples_avail();
|
||||
if( avail <= 0 )
|
||||
{
|
||||
papu->end_frame();
|
||||
avail = papu->samples_avail();
|
||||
}
|
||||
datalen = framesleft>avail?avail:framesleft;
|
||||
datalen = datalen>buf_size?buf_size:datalen;
|
||||
|
||||
long count = papu->read_samples( buf, datalen*2)/2;
|
||||
|
||||
for( fpp_t frame = 0; frame < count; ++frame )
|
||||
{
|
||||
for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
|
||||
{
|
||||
sample_t s = float(buf[frame*2+ch])/32768.0;
|
||||
_working_buffer[frames-framesleft+frame][ch] = s;
|
||||
}
|
||||
}
|
||||
framesleft -= count;
|
||||
}
|
||||
getInstrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void papuInstrument::deleteNotePluginData( notePlayHandle * _n )
|
||||
{
|
||||
delete static_cast<Basic_Gb_Apu *>( _n->m_pluginData );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pluginView * papuInstrument::instantiateView( QWidget * _parent )
|
||||
{
|
||||
return( new papuInstrumentView( this, _parent ) );
|
||||
}
|
||||
|
||||
|
||||
class papuKnob : public knob
|
||||
{
|
||||
public:
|
||||
papuKnob( QWidget * _parent ) :
|
||||
knob( knobStyled, _parent )
|
||||
{
|
||||
setFixedSize( 30, 30 );
|
||||
setCenterPointX( 15.0 );
|
||||
setCenterPointY( 15.0 );
|
||||
setInnerRadius( 8 );
|
||||
setOuterRadius( 13 );
|
||||
setTotalAngle( 270.0 );
|
||||
setLineWidth( 1 );
|
||||
setOuterColor( QColor( 0xF1, 0xFF, 0x93 ) );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
papuInstrumentView::papuInstrumentView( instrument * _instrument,
|
||||
QWidget * _parent ) :
|
||||
instrumentView( _instrument, _parent )
|
||||
{
|
||||
|
||||
setAutoFillBackground( TRUE );
|
||||
QPalette pal;
|
||||
pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) );
|
||||
setPalette( pal );
|
||||
|
||||
m_ch1SweepTimeKnob = new papuKnob( this );
|
||||
m_ch1SweepTimeKnob->setHintText( tr( "Sweep Time:" ) + " ", "" );
|
||||
m_ch1SweepTimeKnob->move( 5 + 4*32, 106 );
|
||||
toolTip::add( m_ch1SweepTimeKnob, tr( "Sweep Time" ) );
|
||||
|
||||
m_ch1SweepRtShiftKnob = new papuKnob( this );
|
||||
m_ch1SweepRtShiftKnob->setHintText( tr( "Sweep RtShift amount:" )
|
||||
+ " ", "" );
|
||||
m_ch1SweepRtShiftKnob->move( 5 + 3*32, 106 );
|
||||
toolTip::add( m_ch1SweepRtShiftKnob, tr( "Sweep RtShift amount" ) );
|
||||
|
||||
m_ch1WavePatternDutyKnob = new papuKnob( this );
|
||||
m_ch1WavePatternDutyKnob->setHintText( tr( "Wave pattern duty:" )
|
||||
+ " ", "" );
|
||||
m_ch1WavePatternDutyKnob->move( 5 + 2*32, 106 );
|
||||
toolTip::add( m_ch1WavePatternDutyKnob, tr( "Wave Pattern Duty" ) );
|
||||
|
||||
m_ch1VolumeKnob = new papuKnob( this );
|
||||
m_ch1VolumeKnob->setHintText( tr( "Square Channel 1 Volume:" )
|
||||
+ " ", "" );
|
||||
m_ch1VolumeKnob->move( 5, 106 );
|
||||
toolTip::add( m_ch1VolumeKnob, tr( "Square Channel 1 Volume:" ) );
|
||||
|
||||
m_ch1SweepStepLengthKnob = new papuKnob( this );
|
||||
m_ch1SweepStepLengthKnob->setHintText( tr( "Length of each step in sweep:" )
|
||||
+ " ", "" );
|
||||
m_ch1SweepStepLengthKnob->move( 5 + 32, 106 );
|
||||
toolTip::add( m_ch1SweepStepLengthKnob, tr( "Length of each step in sweep" ) );
|
||||
|
||||
|
||||
|
||||
m_ch2WavePatternDutyKnob = new papuKnob( this );
|
||||
m_ch2WavePatternDutyKnob->setHintText( tr( "Wave pattern duty:" )
|
||||
+ " ", "" );
|
||||
m_ch2WavePatternDutyKnob->move( 5 + 2*32, 155 );
|
||||
toolTip::add( m_ch2WavePatternDutyKnob, tr( "Wave pattern duty" ) );
|
||||
|
||||
m_ch2VolumeKnob = new papuKnob( this );
|
||||
m_ch2VolumeKnob->setHintText( tr( "Square Channel 2 Volume:" )
|
||||
+ " ", "" );
|
||||
m_ch2VolumeKnob->move( 5, 155 );
|
||||
toolTip::add( m_ch2VolumeKnob, tr( "Square Channel 2 Volume" ) );
|
||||
|
||||
m_ch2SweepStepLengthKnob = new papuKnob( this );
|
||||
m_ch2SweepStepLengthKnob->setHintText( tr( "Length of each step in sweep:" )
|
||||
+ " ", "" );
|
||||
m_ch2SweepStepLengthKnob->move( 5 + 32, 155 );
|
||||
toolTip::add( m_ch2SweepStepLengthKnob, tr( "Length of each step in sweep" ) );
|
||||
|
||||
|
||||
|
||||
m_ch3VolumeKnob = new papuKnob( this );
|
||||
m_ch3VolumeKnob->setHintText( tr( "Wave Channel Volume:" ) + " ", "" );
|
||||
m_ch3VolumeKnob->move( 5, 204 );
|
||||
toolTip::add( m_ch3VolumeKnob, tr( "Wave Channel Volume" ) );
|
||||
|
||||
|
||||
|
||||
m_ch4VolumeKnob = new papuKnob( this );
|
||||
m_ch4VolumeKnob->setHintText( tr( "Noise Channel Volume:" ) + " ", "" );
|
||||
m_ch4VolumeKnob->move( 144, 155 );
|
||||
toolTip::add( m_ch4VolumeKnob, tr( "Noise Channel Volume" ) );
|
||||
|
||||
m_ch4SweepStepLengthKnob = new papuKnob( this );
|
||||
m_ch4SweepStepLengthKnob->setHintText( tr( "Length of each step in sweep:" )
|
||||
+ " ", "" );
|
||||
m_ch4SweepStepLengthKnob->move( 144 + 32, 155 );
|
||||
toolTip::add( m_ch4SweepStepLengthKnob, tr( "Length of each step in sweep" ) );
|
||||
|
||||
|
||||
|
||||
m_so1VolumeKnob = new papuKnob( this );
|
||||
m_so1VolumeKnob->setHintText( tr( "SO1 Volume (Right):" ) + " ", "" );
|
||||
m_so1VolumeKnob->move( 5, 58 );
|
||||
toolTip::add( m_so1VolumeKnob, tr( "SO1 Volume (Right)" ) );
|
||||
|
||||
m_so2VolumeKnob = new papuKnob( this );
|
||||
m_so2VolumeKnob->setHintText( tr( "SO2 Volume (Left):" ) + " ", "" );
|
||||
m_so2VolumeKnob->move( 5 + 32, 58 );
|
||||
toolTip::add( m_so2VolumeKnob, tr( "SO2 Volume (Left)" ) );
|
||||
|
||||
m_trebleKnob = new papuKnob( this );
|
||||
m_trebleKnob->setHintText( tr( "Treble:" ) + " ", "" );
|
||||
m_trebleKnob->move( 5 + 2*32, 58 );
|
||||
toolTip::add( m_trebleKnob, tr( "Treble" ) );
|
||||
|
||||
m_bassKnob = new papuKnob( this );
|
||||
m_bassKnob->setHintText( tr( "Bass:" ) + " ", "" );
|
||||
m_bassKnob->move( 5 + 3*32, 58 );
|
||||
toolTip::add( m_bassKnob, tr( "Bass" ) );
|
||||
|
||||
m_ch1SweepDirButton = new pixmapButton( this, NULL );
|
||||
m_ch1SweepDirButton->setCheckable( TRUE );
|
||||
m_ch1SweepDirButton->move( 167, 108 );
|
||||
m_ch1SweepDirButton->setActiveGraphic(
|
||||
PLUGIN_NAME::getIconPixmap( "btn_down" ) );
|
||||
m_ch1SweepDirButton->setInactiveGraphic(
|
||||
PLUGIN_NAME::getIconPixmap( "btn_up" ) );
|
||||
toolTip::add( m_ch1SweepDirButton, tr( "Sweep Direction" ) );
|
||||
|
||||
m_ch1VolSweepDirButton = new pixmapButton( this, NULL );
|
||||
m_ch1VolSweepDirButton->setCheckable( TRUE );
|
||||
m_ch1VolSweepDirButton->move( 207, 108 );
|
||||
m_ch1VolSweepDirButton->setActiveGraphic(
|
||||
PLUGIN_NAME::getIconPixmap( "btn_up" ) );
|
||||
m_ch1VolSweepDirButton->setInactiveGraphic(
|
||||
PLUGIN_NAME::getIconPixmap( "btn_down" ) );
|
||||
toolTip::add( m_ch1VolSweepDirButton, tr( "Volume Sweep Direction" ) );
|
||||
|
||||
|
||||
|
||||
m_ch2VolSweepDirButton = new pixmapButton( this,
|
||||
tr( "Volume Sweep Direction" ) );
|
||||
m_ch2VolSweepDirButton->setCheckable( TRUE );
|
||||
m_ch2VolSweepDirButton->move( 102, 156 );
|
||||
m_ch2VolSweepDirButton->setActiveGraphic(
|
||||
PLUGIN_NAME::getIconPixmap( "btn_up" ) );
|
||||
m_ch2VolSweepDirButton->setInactiveGraphic(
|
||||
PLUGIN_NAME::getIconPixmap( "btn_down" ) );
|
||||
toolTip::add( m_ch2VolSweepDirButton, tr( "Volume Sweep Direction" ) );
|
||||
|
||||
//m_ch3OnButton = new pixmapButton( this, NULL );
|
||||
//m_ch3OnButton->move( 176, 53 );
|
||||
|
||||
m_ch4VolSweepDirButton = new pixmapButton( this,
|
||||
tr( "Volume Sweep Direction" ) );
|
||||
m_ch4VolSweepDirButton->setCheckable( TRUE );
|
||||
m_ch4VolSweepDirButton->move( 207, 157 );
|
||||
m_ch4VolSweepDirButton->setActiveGraphic(
|
||||
PLUGIN_NAME::getIconPixmap( "btn_up" ) );
|
||||
m_ch4VolSweepDirButton->setInactiveGraphic(
|
||||
PLUGIN_NAME::getIconPixmap( "btn_down" ) );
|
||||
toolTip::add( m_ch4VolSweepDirButton, tr( "Volume Sweep Direction" ) );
|
||||
|
||||
m_ch4ShiftRegWidthButton = new pixmapButton( this, NULL );
|
||||
m_ch4ShiftRegWidthButton->setCheckable( TRUE );
|
||||
m_ch4ShiftRegWidthButton->move( 207, 171 );
|
||||
m_ch4ShiftRegWidthButton->setActiveGraphic(
|
||||
PLUGIN_NAME::getIconPixmap( "btn_7" ) );
|
||||
m_ch4ShiftRegWidthButton->setInactiveGraphic(
|
||||
PLUGIN_NAME::getIconPixmap( "btn_15" ) );
|
||||
toolTip::add( m_ch4ShiftRegWidthButton, tr( "Shift Register Width" ) );
|
||||
|
||||
|
||||
|
||||
|
||||
m_ch1So1Button = new pixmapButton( this, NULL );
|
||||
m_ch1So1Button->setCheckable( TRUE );
|
||||
m_ch1So1Button->move( 208, 51 );
|
||||
m_ch1So1Button->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "btn_on" ) );
|
||||
m_ch1So1Button->setInactiveGraphic( PLUGIN_NAME::getIconPixmap("btn_off") );
|
||||
toolTip::add( m_ch1So1Button, tr( "Channel1 to SO1 (Right)" ) );
|
||||
|
||||
m_ch2So1Button = new pixmapButton( this, NULL );
|
||||
m_ch2So1Button->setCheckable( TRUE );
|
||||
m_ch2So1Button->move( 208, 51 + 12 );
|
||||
m_ch2So1Button->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "btn_on" ) );
|
||||
m_ch2So1Button->setInactiveGraphic( PLUGIN_NAME::getIconPixmap("btn_off") );
|
||||
toolTip::add( m_ch2So1Button, tr( "Channel2 to SO1 (Right)" ) );
|
||||
|
||||
m_ch3So1Button = new pixmapButton( this, NULL );
|
||||
m_ch3So1Button->setCheckable( TRUE );
|
||||
m_ch3So1Button->move( 208, 51 + 2*12 );
|
||||
m_ch3So1Button->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "btn_on" ) );
|
||||
m_ch3So1Button->setInactiveGraphic( PLUGIN_NAME::getIconPixmap("btn_off") );
|
||||
toolTip::add( m_ch3So1Button, tr( "Channel3 to SO1 (Right)" ) );
|
||||
|
||||
m_ch4So1Button = new pixmapButton( this, NULL );
|
||||
m_ch4So1Button->setCheckable( TRUE );
|
||||
m_ch4So1Button->move( 208, 51 + 3*12 );
|
||||
m_ch4So1Button->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "btn_on" ) );
|
||||
m_ch4So1Button->setInactiveGraphic( PLUGIN_NAME::getIconPixmap("btn_off") );
|
||||
toolTip::add( m_ch4So1Button, tr( "Channel4 to SO1 (Right)" ) );
|
||||
|
||||
m_ch1So2Button = new pixmapButton( this, NULL );
|
||||
m_ch1So2Button->setCheckable( TRUE );
|
||||
m_ch1So2Button->move( 148, 51 );
|
||||
m_ch1So2Button->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "btn_on" ) );
|
||||
m_ch1So2Button->setInactiveGraphic( PLUGIN_NAME::getIconPixmap("btn_off") );
|
||||
toolTip::add( m_ch1So2Button, tr( "Channel1 to SO2 (Left)" ) );
|
||||
|
||||
m_ch2So2Button = new pixmapButton( this, NULL );
|
||||
m_ch2So2Button->setCheckable( TRUE );
|
||||
m_ch2So2Button->move( 148, 51 + 12 );
|
||||
m_ch2So2Button->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "btn_on" ) );
|
||||
m_ch2So2Button->setInactiveGraphic( PLUGIN_NAME::getIconPixmap("btn_off") );
|
||||
toolTip::add( m_ch2So2Button, tr( "Channel2 to SO2 (Left)" ) );
|
||||
|
||||
m_ch3So2Button = new pixmapButton( this, NULL );
|
||||
m_ch3So2Button->setCheckable( TRUE );
|
||||
m_ch3So2Button->move( 148, 51 + 2*12 );
|
||||
m_ch3So2Button->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "btn_on" ) );
|
||||
m_ch3So2Button->setInactiveGraphic( PLUGIN_NAME::getIconPixmap("btn_off") );
|
||||
toolTip::add( m_ch3So2Button, tr( "Channel3 to SO2 (Left)" ) );
|
||||
|
||||
m_ch4So2Button = new pixmapButton( this, NULL );
|
||||
m_ch4So2Button->setCheckable( TRUE );
|
||||
m_ch4So2Button->move( 148, 51 + 3*12 );
|
||||
m_ch4So2Button->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "btn_on" ) );
|
||||
m_ch4So2Button->setInactiveGraphic( PLUGIN_NAME::getIconPixmap("btn_off") );
|
||||
toolTip::add( m_ch4So2Button, tr( "Channel4 to SO2 (Left)" ) );
|
||||
|
||||
|
||||
m_graph = new graph( this );
|
||||
m_graph->setGraphStyle( graph::NearestStyle );
|
||||
m_graph->setGraphColor( QColor(0x4E, 0x83, 0x2B) );
|
||||
m_graph->move( 37, 199 );
|
||||
m_graph->resize(208, 47);
|
||||
toolTip::add( m_graph, tr( "Wave Pattern" ) );
|
||||
}
|
||||
|
||||
|
||||
papuInstrumentView::~papuInstrumentView()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void papuInstrumentView::modelChanged( void )
|
||||
{
|
||||
papuInstrument * p = castModel<papuInstrument>();
|
||||
|
||||
m_ch1SweepTimeKnob->setModel( &p->m_ch1SweepTimeModel );
|
||||
m_ch1SweepDirButton->setModel( &p->m_ch1SweepDirModel );
|
||||
m_ch1SweepRtShiftKnob->setModel( &p->m_ch1SweepRtShiftModel );
|
||||
m_ch1WavePatternDutyKnob->setModel( &p->m_ch1WavePatternDutyModel );
|
||||
m_ch1VolumeKnob->setModel( &p->m_ch1VolumeModel );
|
||||
m_ch1VolSweepDirButton->setModel( &p->m_ch1VolSweepDirModel );
|
||||
m_ch1SweepStepLengthKnob->setModel( &p->m_ch1SweepStepLengthModel );
|
||||
|
||||
m_ch2WavePatternDutyKnob->setModel( &p->m_ch2WavePatternDutyModel );
|
||||
m_ch2VolumeKnob->setModel( &p->m_ch2VolumeModel );
|
||||
m_ch2VolSweepDirButton->setModel( &p->m_ch2VolSweepDirModel );
|
||||
m_ch2SweepStepLengthKnob->setModel( &p->m_ch2SweepStepLengthModel );
|
||||
|
||||
//m_ch3OnButton->setModel( &p->m_ch3OnModel );
|
||||
m_ch3VolumeKnob->setModel( &p->m_ch3VolumeModel );
|
||||
|
||||
m_ch4VolumeKnob->setModel( &p->m_ch4VolumeModel );
|
||||
m_ch4VolSweepDirButton->setModel( &p->m_ch4VolSweepDirModel );
|
||||
m_ch4SweepStepLengthKnob->setModel( &p->m_ch4SweepStepLengthModel );
|
||||
m_ch4ShiftRegWidthButton->setModel( &p->m_ch4ShiftRegWidthModel );
|
||||
|
||||
m_so1VolumeKnob->setModel( &p->m_so1VolumeModel );
|
||||
m_so2VolumeKnob->setModel( &p->m_so2VolumeModel );
|
||||
m_ch1So1Button->setModel( &p->m_ch1So1Model );
|
||||
m_ch2So1Button->setModel( &p->m_ch2So1Model );
|
||||
m_ch3So1Button->setModel( &p->m_ch3So1Model );
|
||||
m_ch4So1Button->setModel( &p->m_ch4So1Model );
|
||||
m_ch1So2Button->setModel( &p->m_ch1So2Model );
|
||||
m_ch2So2Button->setModel( &p->m_ch2So2Model );
|
||||
m_ch3So2Button->setModel( &p->m_ch3So2Model );
|
||||
m_ch4So2Button->setModel( &p->m_ch4So2Model );
|
||||
m_trebleKnob->setModel( &p->m_trebleModel );
|
||||
m_bassKnob->setModel( &p->m_bassModel );
|
||||
m_graph->setModel( &p->m_graphModel );
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// neccessary for getting instance out of shared lib
|
||||
plugin * PLUGIN_EXPORT lmms_plugin_main( model *, void * _data )
|
||||
{
|
||||
return( new papuInstrument(
|
||||
static_cast<instrumentTrack *>( _data ) ) );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#include "moc_papu_instrument.cxx"
|
||||
163
plugins/papu/papu_instrument.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* papu_instrument.h - GameBoy papu based instrument
|
||||
*
|
||||
* Copyright (c) 2008 <Attila Herman <attila589/at/gmail.com>
|
||||
* Csaba Hruska <csaba.hruska/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PAPU_H
|
||||
#define _PAPU_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include "instrument.h"
|
||||
#include "instrument_view.h"
|
||||
#include "knob.h"
|
||||
#include "graph.h"
|
||||
|
||||
class papuInstrumentView;
|
||||
class notePlayHandle;
|
||||
class pixmapButton;
|
||||
|
||||
class papuInstrument : public instrument
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
papuInstrument( instrumentTrack * _instrument_track );
|
||||
virtual ~papuInstrument();
|
||||
|
||||
virtual void playNote( notePlayHandle * _n, bool _try_parallelizing,
|
||||
sampleFrame * _working_buffer );
|
||||
virtual void deleteNotePluginData( notePlayHandle * _n );
|
||||
|
||||
|
||||
virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
|
||||
virtual void loadSettings( const QDomElement & _this );
|
||||
|
||||
virtual QString nodeName( void ) const;
|
||||
|
||||
virtual f_cnt_t desiredReleaseFrames( void ) const;
|
||||
|
||||
virtual pluginView * instantiateView( QWidget * _parent );
|
||||
|
||||
|
||||
/*public slots:
|
||||
void updateKnobHint( void );
|
||||
void updateKnobToolTip( void );*/
|
||||
|
||||
private:
|
||||
knobModel m_ch1SweepTimeModel;
|
||||
boolModel m_ch1SweepDirModel;
|
||||
knobModel m_ch1SweepRtShiftModel;
|
||||
knobModel m_ch1WavePatternDutyModel;
|
||||
knobModel m_ch1VolumeModel;
|
||||
boolModel m_ch1VolSweepDirModel;
|
||||
knobModel m_ch1SweepStepLengthModel;
|
||||
|
||||
knobModel m_ch2WavePatternDutyModel;
|
||||
knobModel m_ch2VolumeModel;
|
||||
boolModel m_ch2VolSweepDirModel;
|
||||
knobModel m_ch2SweepStepLengthModel;
|
||||
|
||||
boolModel m_ch3OnModel;
|
||||
knobModel m_ch3VolumeModel;
|
||||
|
||||
knobModel m_ch4VolumeModel;
|
||||
boolModel m_ch4VolSweepDirModel;
|
||||
knobModel m_ch4SweepStepLengthModel;
|
||||
knobModel m_ch4ShiftClockFreqModel;
|
||||
boolModel m_ch4ShiftRegWidthModel;
|
||||
knobModel m_ch4FreqDivRatioModel;
|
||||
|
||||
knobModel m_so1VolumeModel;
|
||||
knobModel m_so2VolumeModel;
|
||||
boolModel m_ch1So1Model;
|
||||
boolModel m_ch2So1Model;
|
||||
boolModel m_ch3So1Model;
|
||||
boolModel m_ch4So1Model;
|
||||
boolModel m_ch1So2Model;
|
||||
boolModel m_ch2So2Model;
|
||||
boolModel m_ch3So2Model;
|
||||
boolModel m_ch4So2Model;
|
||||
knobModel m_trebleModel;
|
||||
knobModel m_bassModel;
|
||||
|
||||
graphModel m_graphModel;
|
||||
|
||||
friend class papuInstrumentView;
|
||||
} ;
|
||||
|
||||
|
||||
class papuInstrumentView : public instrumentView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
papuInstrumentView( instrument * _instrument, QWidget * _parent );
|
||||
virtual ~papuInstrumentView();
|
||||
|
||||
private:
|
||||
virtual void modelChanged( void );
|
||||
|
||||
knob * m_ch1SweepTimeKnob;
|
||||
pixmapButton * m_ch1SweepDirButton;
|
||||
knob * m_ch1SweepRtShiftKnob;
|
||||
knob * m_ch1WavePatternDutyKnob;
|
||||
knob * m_ch1VolumeKnob;
|
||||
pixmapButton * m_ch1VolSweepDirButton;
|
||||
knob * m_ch1SweepStepLengthKnob;
|
||||
|
||||
knob * m_ch2WavePatternDutyKnob;
|
||||
knob * m_ch2VolumeKnob;
|
||||
pixmapButton * m_ch2VolSweepDirButton;
|
||||
knob * m_ch2SweepStepLengthKnob;
|
||||
|
||||
pixmapButton * m_ch3OnButton;
|
||||
knob * m_ch3VolumeKnob;
|
||||
|
||||
knob * m_ch4VolumeKnob;
|
||||
pixmapButton * m_ch4VolSweepDirButton;
|
||||
knob * m_ch4SweepStepLengthKnob;
|
||||
knob * m_ch4ShiftClockFreqKnob;
|
||||
pixmapButton * m_ch4ShiftRegWidthButton;
|
||||
knob * m_ch4FreqDivRatioKnob;
|
||||
|
||||
knob * m_so1VolumeKnob;
|
||||
knob * m_so2VolumeKnob;
|
||||
pixmapButton * m_ch1So1Button;
|
||||
pixmapButton * m_ch2So1Button;
|
||||
pixmapButton * m_ch3So1Button;
|
||||
pixmapButton * m_ch4So1Button;
|
||||
pixmapButton * m_ch1So2Button;
|
||||
pixmapButton * m_ch2So2Button;
|
||||
pixmapButton * m_ch3So2Button;
|
||||
pixmapButton * m_ch4So2Button;
|
||||
knob * m_trebleKnob;
|
||||
knob * m_bassKnob;
|
||||
|
||||
graph * m_graph;
|
||||
|
||||
/*protected slots:
|
||||
void updateKnobHint( void );
|
||||
void updateKnobToolTip( void );*/
|
||||
} ;
|
||||
|
||||
|
||||
#endif
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.9 KiB |