diff -bNaHudr bla1/Makefile.am bla2/Makefile.am --- bla1/Makefile.am 2007-06-07 20:32:52.000000000 +0300 +++ bla2/Makefile.am 2006-08-29 19:24:53.000000000 +0300 @@ -2,10 +2,15 @@ bin_PROGRAMS = fceu fceu_SOURCES = x6502.c fceustr.c cart.c cheat.c crc32.c debug.c endian.c fceu.c fds.c file.c filter.c general.c ines.c input.c md5.c memory.c netplay.c nsf.c palette.c ppu.c sound.c state.c unif.c video.c vsuni.c wave.c movie.c - -# soundexp.c - fceu_SOURCES += unzip.c +fceu_SOURCES += blarg-sound/BlarggApu.cpp +fceu_SOURCES += blarg-sound/Sound_Queue.cpp +fceu_SOURCES += blarg-sound/nes_apu/Blip_Buffer.cpp +fceu_SOURCES += blarg-sound/nes_apu/Nes_Apu.cpp +fceu_SOURCES += blarg-sound/nes_apu/Nes_Fme7_Apu.cpp +fceu_SOURCES += blarg-sound/nes_apu/Nes_Namco_Apu.cpp +fceu_SOURCES += blarg-sound/nes_apu/Nes_Oscs.cpp +fceu_SOURCES += blarg-sound/nes_apu/Nes_Vrc6_Apu.cpp include boards/Makefile.am.inc include input/Makefile.am.inc @@ -25,4 +31,5 @@ fceu_SOURCES += $(fceud_SOURCES) -DEFAULT_INCLUDES = +DEFAULT_INCLUDES = -Iblarg-sound + diff -bNaHudr bla1/blarg-sound/BlarggApu.cpp bla2/blarg-sound/BlarggApu.cpp --- bla1/blarg-sound/BlarggApu.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/BlarggApu.cpp 2007-06-07 19:54:21.000000000 +0300 @@ -0,0 +1,480 @@ +#include "nes_apu/Nes_Apu.h" +#include "nes_apu/Blip_Buffer.h" +#include "nes_apu/Nes_Vrc6_Apu.h" +#include "nes_apu/Nes_Namco_Apu.h" +#include "nes_apu/Nes_Fme7_Apu.h" +#include "BlarggApu.h" + +#include +#include + +extern "C" { + namespace FCEU { + #include "../types.h" + #include "../x6502.h" + #include "../fceu.h" + } +} + +static long FinalRate = 48000; +static long ContempRate = 48000; + +static int SimpleDMCreader(void*, nes_addr_t addr) +{ + return FCEU::ARead[addr](addr); +} + +static bool VRC6Enabled = false; +static bool NamcoEnabled = false; +static bool FME7Enabled = false; + +static class gruu +{ +public: + gruu() : Disabled(true) + { + ReInitTimers(); + } + + void ReInitTimers() + { + double gain = 1.4; + + apu.reset(FCEU::PAL); + + apu.dmc_reader(SimpleDMCreader, 0); + apu.output(&buf); + + if(FME7Enabled) + { + fme7.output(&buf); + gain *= 0.75; + } + if(VRC6Enabled) + { + vrc6.output(&buf); + gain *= 0.75; + } + if(NamcoEnabled) + { + namco.output(&buf); + gain *= 0.75; + } + + const double clockrate_fp + = FCEU::PAL ? 1662607.0 + : 19687500.0 / 11.0; + const double fps + = FCEU::PAL ? 1662607.0 / 33247.5 + : 39375000.0 / 655171.0; + + this->frame_len = FCEU::PAL ? 33247.5 : 29780.5; + + fprintf(stderr, "Set clock rate. %.4f; %.2f ticks per frame. Gain=%g\n", + clockrate_fp, this->frame_len, gain); + buf.clock_rate(clockrate_fp); + + NewFrame(); + + fme7.volume(gain); + vrc6.volume(gain); + namco.volume(gain); + apu.volume(gain); + } + + void SetSampleRate(long rate) + { + Disabled=false; + fprintf(stderr, "Blargg Init with rate %ld\n", rate); + ReInitTimers(); + buf.sample_rate(rate); + //buf.enable_nonlinearity(apu); + } + + void WriteReg(unsigned addr, int data) + { + if(Disabled) return; + + /* TIMER: Number of cycles since the last call of end_frame */ + if(FME7Enabled) + { + if(addr == 0xC000) { /*fprintf(stderr,"Gimmick write latch %d\n", data);*/ fme7.write_latch(data); return; } + if(addr == 0xE000) { /*fprintf(stderr,"Gimmick write data %02X\n", data);*/ fme7.write_data(GetElapsed(), data); return; } + } + if(VRC6Enabled) + { + if(addr >= 0x9000 && addr <= 0x9002) { vrc6.write_osc(GetElapsed(), 0, addr&3, data); return; } + if(addr >= 0xA000 && addr <= 0xA002) { vrc6.write_osc(GetElapsed(), 1, addr&3, data); return; } + if(addr >= 0xB000 && addr <= 0xB002) { vrc6.write_osc(GetElapsed(), 2, addr&3, data); return; } + } + if(NamcoEnabled) + { + if(addr == Nes_Namco_Apu::data_reg_addr) namco.write_data(GetElapsed(), data); + if(addr == Nes_Namco_Apu::addr_reg_addr) namco.write_addr(data); + } + /* fprintf(stderr, "%d: write[%04X]<-%02X\n", GetElapsed(),addr,data); */ + apu.write_register(GetElapsed(), addr, data); + } + int ReadReg() + { + if(Disabled) return 0; + + if(NamcoEnabled) + namco.read_data(); + + return apu.read_status(GetElapsed()); + } + void NextFrame() + { + if(Disabled) return; + + frame_len_got += frame_len; + unsigned frame_length = (unsigned)frame_len_got; + /* + fprintf(stderr, "frame_len=%u (got %.2f, added %.2f)\n", + frame_length, frame_len_got, frame_len); + */ + apu.end_frame(frame_length); + if(FME7Enabled) fme7.end_frame(frame_length); + if(VRC6Enabled) vrc6.end_frame(frame_length); + if(NamcoEnabled) namco.end_frame(frame_length); + + buf.end_frame(frame_length); + + frame_len_got -= frame_length; + + NewFrame(); + } + + void PrepareForCycles(unsigned num_cycles) + { + /* Assume that the time that was last prepared, + * has been elapsed already + */ + OffsetSoFar += PreparedFor; + PreparedFor = num_cycles; + } + + long SamplesAvail() const + { + return buf.samples_avail(); + } + + long ReadSamples( blip_sample_t* p, long s ) + { + return buf.read_samples( p, s ); + } +private: + void NewFrame() + { + OffsetSoFar = 0; + PreparedFor = 0; + DummyTime = 0; + } + int GetElapsed() + { + DummyTime += 4; + return DummyTime; + /* In practice, it looks like this dummy code above + * provides better audio quality than the more correct + * code below. I don't know why! -Bisqwit + */ + + int remain = FCEU::X.count; + int elapsed = PreparedFor - remain; + /* + fprintf(stderr, "Elapsed: prepared for %d, remain %d, offset %d\n", + PreparedFor, remain, OffsetSoFar); + */ + return (elapsed + OffsetSoFar) / 48; + } + +private: + Nes_Apu apu; + Blip_Buffer buf; + + /* Cycle counters */ + unsigned OffsetSoFar; + unsigned PreparedFor; + unsigned DummyTime; + + double frame_len; + double frame_len_got; + +private: + Nes_Fme7_Apu fme7; + Nes_Vrc6_Apu vrc6; + Nes_Namco_Apu namco; + + bool Disabled; + +} BlarggAPU; + + +void BlarggSetSampleRate(long rate) +{ + FinalRate = rate; + //ContempRate = rate; + BlarggAPU.SetSampleRate(ContempRate); + + fprintf(stderr, "Contemp=%d, Final=%d\n", ContempRate, FinalRate); +} + +void BlarggWrite(unsigned addr, int data) +{ + BlarggAPU.WriteReg(addr, data); +} + +int BlarggRead() +{ + return BlarggAPU.ReadReg(); +} + +void BlarggWillExecute(unsigned num_cycles) +{ + BlarggAPU.PrepareForCycles(num_cycles); +} + +void BlarggEnableVRC6() +{ + if(VRC6Enabled) return; + VRC6Enabled = true; + BlarggSetSampleRate(FinalRate); + fprintf(stderr, "VRC6 synthesizer enabled\n"); +} + +void BlarggEnableNamco() +{ + if(NamcoEnabled) return; + NamcoEnabled = true; + BlarggSetSampleRate(FinalRate); + fprintf(stderr, "Namco synthesizer enabled\n"); +} + +void BlarggEnableFME7() +{ + if(FME7Enabled) return; + FME7Enabled = true; + BlarggSetSampleRate(FinalRate); + fprintf(stderr, "FME7 synthesizer enabled\n"); +} + +/************* AUDIO RESAMPLE CODE ****************/ +static inline double dblmin(const double a, const double b) { return a < b ? a : b; } +static inline double dblmax(const double a, const double b) { return a > b ? a : b; } +static inline double dblabs(const double a) { return a < 0 ? -a : a; } +static inline double Lanczos(double x) +{ + if(x == 0.0) return 1.0; + if(x <= -3.0 || x >= 3.0)return 0.0; + double tmp = x * M_PI, tmp2 = tmp / 3.0; + return sin(tmp) * sin(tmp2) / (tmp * tmp2); +} + +typedef short ResampleItem; +struct ContiguousResampleBuf +{ +public: + double center; +public: + ContiguousResampleBuf() : center(0.5), firstpos(0) { } + + inline void NotNeedOlderThan(int pos) + { + const int n_discard_at_once = 4096; + int discardable = pos-firstpos; + if(discardable >= n_discard_at_once) + { + int n_discard = (discardable / n_discard_at_once) * n_discard_at_once; + AudioData.erase(AudioData.begin(), AudioData.begin()+n_discard); + firstpos += n_discard; + } + } + inline bool AddMul(double& t, int pos, double coeff) + { + if(pos >= firstpos) + { + if(pos >= (int)(firstpos + AudioData.size())) + return false; + t += AudioData[pos - firstpos] * coeff; + } + return true; + } + inline void Feed(const unsigned char* input, unsigned nbytes) + { + AudioData.insert(AudioData.end(), + (ResampleItem*)input, + (ResampleItem*)(input+nbytes)); + } + inline int DropExcess() + { + //printf("excess=%d, center=%g\n", firstpos, center); + int res = firstpos; + firstpos = 0; + return res; + } +private: + std::vector AudioData; + int firstpos; +}; +const std::vector + ContiguousResample(double factor, + const unsigned char* input, unsigned nbytes) +{ + static ContiguousResampleBuf status; + + if(factor == 1.0) + { + return std::vector + ( (ResampleItem*)input, + (ResampleItem*)(input+nbytes) ); + } + + status.Feed(input, nbytes); + + const double filter_support = 3.0; + double scale = dblmin(factor, 1.0); + double support = filter_support / scale; + if(support <= 0.5) { support = 0.5 + 1E-12; scale = 1.0; } + + const double factor_rev = 1.0 / factor; + double contribution[(unsigned)(support*2 + 2)]; + + std::vector result; + for(;;) + { + const int start = (int)(status.center-support+0.5); + const int stop = (int)(status.center+support+0.5); + double density = 0.0; + + unsigned nmax = stop-start; + + double s = start - status.center + 0.5; + for(unsigned n=0; n32767)res_t=32767; + result.push_back(res_t); + } +NoMoreData: + status.center -= status.DropExcess(); + return result; +} + +static class Feeder +{ +private: + std::vector buffer; +public: + Feeder() : buf(0), bufsize(0) + { + } + void FeedSet() + { + if(buf) return; + + long avail = BlarggAPU.SamplesAvail(); + if(avail < 0) avail = 0; + + /* + if(avail) + { + static double SamplesTotal=0; static unsigned FramesTotal=0; + SamplesTotal += avail; + ++FramesTotal; + static unsigned counter=0; + if(++counter>=20){counter=0; + fprintf(stderr, "Average samples per frame: %.4f (this frame: %ld)\n", SamplesTotal/FramesTotal, avail); + } + } + else + fprintf(stderr, "No samples this frame\n"); + */ + + if(FinalRate == ContempRate) + { + buffer.resize(avail); + long size = BlarggAPU.ReadSamples(&buffer[0], buffer.size()); + if(size < 0) size = 0; + buffer.resize(size*2); + + for(long s = size; s-- > 0; ) + { + buffer[s*2+0] = buffer[s]; + buffer[s*2+1] = buffer[s]; + } + this->buf = (void*)&buffer[0]; + this->bufsize = buffer.size()/2; + return; + } + + std::vector tmp(avail); + long tsize = BlarggAPU.ReadSamples(&tmp[0], tmp.size()); + if(tsize < 0) tsize = 0; + tmp.resize(tsize); + + std::vector result = + ContiguousResample(FinalRate / (double)ContempRate, + (const unsigned char*)&tmp[0], + tmp.size() * sizeof(tmp[0])); + + long size = result.size(); + buffer.resize(size*2); + for(long s = size; s-- > 0; ) + { + buffer[s*2+0] = result[s]; + buffer[s*2+1] = result[s]; + } + this->buf = (void*)&buffer[0]; + this->bufsize = buffer.size()/2; + } + void FeedClear() + { + buf=0; + bufsize=0; + } +public: + void* buf; + long bufsize; +} Feeder; + + + +unsigned BlarggGetSamplesAvail(void) +{ + if(!Feeder.buf) Feeder.FeedSet(); + return Feeder.bufsize; +} + +void* BlarggGetSoundBuffer(void) +{ + if(!Feeder.buf) Feeder.FeedSet(); + return Feeder.buf; +} + +void BlarggEndFrame() +{ + BlarggAPU.NextFrame(); + Feeder.FeedClear(); +} + diff -bNaHudr bla1/blarg-sound/BlarggApu.h bla2/blarg-sound/BlarggApu.h --- bla1/blarg-sound/BlarggApu.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/BlarggApu.h 2006-08-17 23:42:41.000000000 +0300 @@ -0,0 +1,20 @@ +#ifdef __cplusplus +extern "C" { +#endif +void BlarggSetSampleRate(long rate); +void BlarggWrite(unsigned addr, int data); +int BlarggRead(void); +void BlarggEndFrame(void); + +void BlarggEnableVRC6(); +void BlarggEnableNamco(); +void BlarggEnableFME7(); + +void BlarggWillExecute(unsigned num_cycles); + +unsigned BlarggGetSamplesAvail(void); +void* BlarggGetSoundBuffer(void); + +#ifdef __cplusplus +} +#endif diff -bNaHudr bla1/blarg-sound/LGPL.txt bla2/blarg-sound/LGPL.txt --- bla1/blarg-sound/LGPL.txt 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/LGPL.txt 2001-07-15 12:55:08.000000000 +0300 @@ -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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff -bNaHudr bla1/blarg-sound/Simple_Apu.cpp bla2/blarg-sound/Simple_Apu.cpp --- bla1/blarg-sound/Simple_Apu.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/Simple_Apu.cpp 2006-08-17 19:33:24.000000000 +0300 @@ -0,0 +1,90 @@ + +// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ + +#include "Simple_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 LesserGeneral 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 */ + +static int null_dmc_reader( void*, cpu_addr_t ) +{ + return 0x55; // causes dmc sample to be flat +} + +Simple_Apu::Simple_Apu() +{ + time = 0; +#if 0 /* NTSC */ + frame_length = 29780; +#else + frame_length = 33246; +#endif + apu.dmc_reader( null_dmc_reader, NULL ); +} + +Simple_Apu::~Simple_Apu() +{ +} + +void Simple_Apu::dmc_reader( int (*f)( void* user_data, cpu_addr_t ), void* p ) +{ + assert( f ); + apu.dmc_reader( f, p ); +} + +blargg_err_t Simple_Apu::sample_rate( long rate ) +{ + apu.output( &buf ); +#if 0 /* NTSC */ + buf.clock_rate( 1789773 ); +#else + buf.clock_rate( 1662607 ); +#endif + return buf.sample_rate( rate ); +} + +void Simple_Apu::write_register( cpu_addr_t addr, int data ) +{ + apu.write_register( clock(), addr, data ); +} + +int Simple_Apu::read_status() +{ + return apu.read_status( clock() ); +} + +void Simple_Apu::end_frame() +{ + time = 0; + frame_length ^= 1; + apu.end_frame( frame_length+1 ); + buf.end_frame( frame_length+1 ); +} + +long Simple_Apu::samples_avail() const +{ + return buf.samples_avail(); +} + +long Simple_Apu::read_samples( sample_t* p, long s ) +{ + return buf.read_samples( p, s ); +} + +void Simple_Apu::save_snapshot( apu_snapshot_t* out ) const +{ + apu.save_snapshot( out ); +} + +void Simple_Apu::load_snapshot( apu_snapshot_t const& in ) +{ + apu.load_snapshot( in ); +} + diff -bNaHudr bla1/blarg-sound/Simple_Apu.h bla2/blarg-sound/Simple_Apu.h --- bla1/blarg-sound/Simple_Apu.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/Simple_Apu.h 2006-08-15 22:13:56.000000000 +0300 @@ -0,0 +1,56 @@ + +// NES 2A03 APU sound chip emulator with simpler interface + +// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. + +#ifndef SIMPLE_APU_H +#define SIMPLE_APU_H + +#include "nes_apu/Nes_Apu.h" +#include "nes_apu/Blip_Buffer.h" + +class Simple_Apu { +public: + Simple_Apu(); + ~Simple_Apu(); + + // This simpler interface works well for most games. Some benefit from + // the higher precision of the full Nes_Apu interface, which provides + // clock-cycle accurate register read/write and IRQ timing functions. + + // Set function for APU to call when it needs to read memory (DMC samples) + void dmc_reader( int (*callback)( void* user_data, cpu_addr_t ), void* user_data = NULL ); + + // Set output sample rate + blargg_err_t sample_rate( long rate ); + + // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) + void write_register( cpu_addr_t, int data ); + + // Read from status register at 0x4015 + int read_status(); + + // End a 1/60 sound frame + void end_frame(); + + // Number of samples in buffer + long samples_avail() const; + + // Read at most 'count' samples and return number of samples actually read + typedef blip_sample_t sample_t; + long read_samples( sample_t* buf, long buf_size ); + + // Save/load snapshot of emulation state + void save_snapshot( apu_snapshot_t* out ) const; + void load_snapshot( apu_snapshot_t const& ); + +private: + Nes_Apu apu; + Blip_Buffer buf; + blip_time_t time; + blip_time_t frame_length; + blip_time_t clock() { return time += 4; } +}; + +#endif + diff -bNaHudr bla1/blarg-sound/Sound_Queue.cpp bla2/blarg-sound/Sound_Queue.cpp --- bla1/blarg-sound/Sound_Queue.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/Sound_Queue.cpp 2005-10-03 14:59:30.000000000 +0300 @@ -0,0 +1,138 @@ + +// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ + +#include "Sound_Queue.h" + +#include +#include + +/* Copyright (C) 2005 by Shay Green. Permission is hereby granted, free of +charge, to any person obtaining a copy of this software module and associated +documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the +following conditions: The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. THE +SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +// Return current SDL_GetError() string, or str if SDL didn't have a string +static const char* sdl_error( const char* str ) +{ + const char* sdl_str = SDL_GetError(); + if ( sdl_str && *sdl_str ) + str = sdl_str; + return str; +} + +Sound_Queue::Sound_Queue() +{ + bufs = NULL; + free_sem = NULL; + write_buf = 0; + write_pos = 0; + read_buf = 0; + sound_open = false; +} + +Sound_Queue::~Sound_Queue() +{ + if ( sound_open ) + { + SDL_PauseAudio( true ); + SDL_CloseAudio(); + } + + if ( free_sem ) + SDL_DestroySemaphore( free_sem ); + + delete [] bufs; +} + +int Sound_Queue::sample_count() const +{ + int buf_free = SDL_SemValue( free_sem ) * buf_size + (buf_size - write_pos); + return buf_size * buf_count - buf_free; +} + +const char* Sound_Queue::init( long sample_rate, int chan_count ) +{ + assert( !bufs ); // can only be initialized once + + bufs = new sample_t [(long) buf_size * buf_count]; + if ( !bufs ) + return "Out of memory"; + + free_sem = SDL_CreateSemaphore( buf_count - 1 ); + if ( !free_sem ) + return sdl_error( "Couldn't create semaphore" ); + + SDL_AudioSpec as; + as.freq = sample_rate; + as.format = AUDIO_S16SYS; + as.channels = chan_count; + as.silence = 0; + as.samples = buf_size; + as.size = 0; + as.callback = fill_buffer_; + as.userdata = this; + if ( SDL_OpenAudio( &as, NULL ) < 0 ) + return sdl_error( "Couldn't open SDL audio" ); + SDL_PauseAudio( false ); + sound_open = true; + + return NULL; +} + +inline Sound_Queue::sample_t* Sound_Queue::buf( int index ) +{ + assert( (unsigned) index < buf_count ); + return bufs + (long) index * buf_size; +} + +void Sound_Queue::write( const sample_t* in, int count ) +{ + while ( count ) + { + int n = buf_size - write_pos; + if ( n > count ) + n = count; + + memcpy( buf( write_buf ) + write_pos, in, n * sizeof (sample_t) ); + in += n; + write_pos += n; + count -= n; + + if ( write_pos >= buf_size ) + { + write_pos = 0; + write_buf = (write_buf + 1) % buf_count; + SDL_SemWait( free_sem ); + } + } +} + +void Sound_Queue::fill_buffer( Uint8* out, int count ) +{ + if ( SDL_SemValue( free_sem ) < buf_count - 1 ) + { + memcpy( out, buf( read_buf ), count ); + read_buf = (read_buf + 1) % buf_count; + SDL_SemPost( free_sem ); + } + else + { + memset( out, 0, count ); + } +} + +void Sound_Queue::fill_buffer_( void* user_data, Uint8* out, int count ) +{ + ((Sound_Queue*) user_data)->fill_buffer( out, count ); +} + diff -bNaHudr bla1/blarg-sound/Sound_Queue.h bla2/blarg-sound/Sound_Queue.h --- bla1/blarg-sound/Sound_Queue.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/Sound_Queue.h 2005-10-03 15:09:10.000000000 +0300 @@ -0,0 +1,44 @@ + +// Simple sound queue for synchronous sound handling in SDL + +// Copyright (C) 2005 Shay Green. MIT license. + +#ifndef SOUND_QUEUE_H +#define SOUND_QUEUE_H + +#include "SDL.h" + +// Simple SDL sound wrapper that has a synchronous interface +class Sound_Queue { +public: + Sound_Queue(); + ~Sound_Queue(); + + // Initialize with specified sample rate and channel count. + // Returns NULL on success, otherwise error string. + const char* init( long sample_rate, int chan_count = 1 ); + + // Number of samples in buffer waiting to be played + int sample_count() const; + + // Write samples to buffer and block until enough space is available + typedef short sample_t; + void write( const sample_t*, int count ); + +private: + enum { buf_size = 2048 }; + enum { buf_count = 3 }; + sample_t* volatile bufs; + SDL_sem* volatile free_sem; + int volatile read_buf; + int write_buf; + int write_pos; + bool sound_open; + + sample_t* buf( int index ); + void fill_buffer( Uint8*, int ); + static void fill_buffer_( void*, Uint8*, int ); +}; + +#endif + diff -bNaHudr bla1/blarg-sound/VERSIONS bla2/blarg-sound/VERSIONS --- bla1/blarg-sound/VERSIONS 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/VERSIONS 2006-10-15 13:46:57.000000000 +0300 @@ -0,0 +1,16 @@ +Game-music-emu 0.3.0 + [[Blip buffer 0.4.0 + CONFIRMED]] + [[NES snd apu 0.1.7 approximately + VERSION STRINGS]] + + EVERYTHING, EXCEPT TYPES: + double for clock_rate, instead of long + + clocks (blip_time_t, blip_resample_time_t, factor, etc) + measured in long long, not long + + BLIP_BUFFER_ACCURACY is 29, not 16 + BLIP_PHASE_BITS is 7, not 6 + + Default freq is 48000 not 44100 diff -bNaHudr bla1/blarg-sound/Wave_Writer.cpp bla2/blarg-sound/Wave_Writer.cpp --- bla1/blarg-sound/Wave_Writer.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/Wave_Writer.cpp 2005-06-18 19:09:06.000000000 +0300 @@ -0,0 +1,117 @@ + +// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ + +#include "Wave_Writer.hpp" + +#include +#include + +/* Copyright (C) 2003-2005 by Shay Green. Permission is hereby granted, free +of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the +following conditions: The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. THE +SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +const int header_size = 0x2C; + +static void exit_with_error( const char* str ) { + fprintf( stderr, "Error: %s\n", str ); + exit( EXIT_FAILURE ); +} + +Wave_Writer::Wave_Writer( long sample_rate, const char* filename ) +{ + sample_count_ = 0; + rate = sample_rate; + buf_pos = header_size; + stereo( 0 ); + + buf = new unsigned char [buf_size]; + if ( !buf ) + exit_with_error( "Out of memory" ); + + file = fopen( filename, "wb" ); + if ( !file ) + exit_with_error( "Couldn't open WAVE file for writing" ); +} + +void Wave_Writer::flush() +{ + if ( buf_pos && !fwrite( buf, buf_pos, 1, file ) ) + exit_with_error( "Couldn't write WAVE data" ); + buf_pos = 0; +} + +void Wave_Writer::write( const sample_t* in, long remain, int skip ) +{ + sample_count_ += remain; + while ( remain ) + { + if ( buf_pos >= buf_size ) + flush(); + + long n = (unsigned long) (buf_size - buf_pos) / sizeof (sample_t); + if ( n > remain ) + n = remain; + remain -= n; + + // convert to lsb first format + unsigned char* p = &buf [buf_pos]; + while ( n-- ) { + int s = *in; + in += skip; + *p++ = (unsigned char) s; + *p++ = (unsigned char) (s >> 8); + } + + buf_pos = p - buf; + assert( buf_pos <= buf_size ); + } +} + +Wave_Writer::~Wave_Writer() +{ + flush(); + + // generate header + long ds = sample_count_ * sizeof (sample_t); + long rs = header_size - 8 + ds; + int frame_size = chan_count * sizeof (sample_t); + long bps = rate * frame_size; + unsigned char header [header_size] = { + 'R','I','F','F', + rs,rs>>8, // length of rest of file + rs>>16,rs>>24, + 'W','A','V','E', + 'f','m','t',' ', + 0x10,0,0,0, // size of fmt chunk + 1,0, // uncompressed format + chan_count,0, // channel count + rate,rate >> 8, // sample rate + rate>>16,rate>>24, + bps,bps>>8, // bytes per second + bps>>16,bps>>24, + frame_size,0, // bytes per sample frame + 16,0, // bits per sample + 'd','a','t','a', + ds,ds>>8,ds>>16,ds>>24// size of sample data + // ... // sample data + }; + + // write header + fseek( file, 0, SEEK_SET ); + fwrite( header, sizeof header, 1, file ); + + fclose( file ); + delete [] buf; +} + diff -bNaHudr bla1/blarg-sound/Wave_Writer.hpp bla2/blarg-sound/Wave_Writer.hpp --- bla1/blarg-sound/Wave_Writer.hpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/Wave_Writer.hpp 2005-05-18 00:02:04.000000000 +0300 @@ -0,0 +1,57 @@ + +// WAVE sound file writer for recording 16-bit output during program development + +// Copyright (C) 2003-2004 Shay Green. MIT license. + +#ifndef WAVE_WRITER_HPP +#define WAVE_WRITER_HPP + +#include +#include + +class Wave_Writer { +public: + typedef short sample_t; + + // Create sound file with given sample rate (in Hz) and filename. + // Exit program if there's an error. + Wave_Writer( long sample_rate, char const* filename = "out.wav" ); + + // Enable stereo output + void stereo( int ); + + // Append 'count' samples to file. Use every 'skip'th source sample; allows + // one channel of stereo sample pairs to be written by specifying a skip of 2. + void write( const sample_t*, long count, int skip = 1 ); + + // Number of samples written so far + long sample_count() const; + + // Write sound file header and close file. If no samples were written, + // delete file. + ~Wave_Writer(); + + +// End of public interface +private: + enum { buf_size = 32768 * 2 }; + unsigned char* buf; + FILE* file; + long sample_count_; + long rate; + long buf_pos; + int chan_count; + + void flush(); +}; + +inline void Wave_Writer::stereo( int s ) { + chan_count = s ? 2 : 1; +} + +inline long Wave_Writer::sample_count() const { + return sample_count_; +} + +#endif + diff -bNaHudr bla1/blarg-sound/changes.txt bla2/blarg-sound/changes.txt --- bla1/blarg-sound/changes.txt 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/changes.txt 2005-10-03 16:08:06.000000000 +0300 @@ -0,0 +1,132 @@ +Nes_Snd_Emu Change Log +---------------------- + +Nes_Snd_Emu 0.1.7 +----------------- +- Created discussion forum: http://groups.google.com/group/blargg-sound-libs + +- Added simplified version of APU to make it easy to get started + +- Added Konami VRC6 and Namco 106 sound chip emulators + +- Added support for APU's non-linear output + +- Added demo of using SDL sound, along with helpful Sound_Queue + +- Improved documentation + +- Redid state snapshot support + + +Nes_Snd_Emu 0.1.5 +----------------- +- Changed licensing to GNU Lesser General Public License (LGPL) + +- Partially implemented DMC non-linearity when its value is directly set using +$4011, which reduces previously over-emphasized "popping" of percussion on some +games (TMNT II in particular) + +- Implemented workaround for MSVC++ 6 compiler limitations, allowing it to work +on that compiler again + +- Changed error return value of Blip_Buffer::sample_rate() to blargg_err_t +(defined in blargg_common.h), to make error reporting consistent with other +functions. This means the "no error" return value is the opposite of what it +was before, which will break current code which checks the error return value: + + // current code (broken) + if ( !buf.sample_rate( samples_per_sec ) ) + out_of_memory(); + + // quick-and-dirty fix (just remove the ! operation) + if ( buf.sample_rate( samples_per_sec ) ) + out_of_memory(); + + // proper fix + blargg_err_t error = buf.sample_rate( samples_per_sec ); + if ( error ) + report_error( error ); + +- Added sample clamping to Blip_Buffer::read_samples(), allowing higher volume +with little distortion + +- Changed 'size_t' values in Blip_Buffer interface to 'long' + +- Changed demo to generate a WAVE sound file rather than an AIFF file + +- Updated to Blip_Buffer 0.3.2 + + +Nes_Snd_Emu 0.1.4 +----------------- +- Moved NSF player-related modules to Game_Music_Emu library + +- Added note about compiler optimizer bugs and workaround for one that was +found + +- Nes_Apu::write_register() now takes the actual memory address, rather than +register number (i.e. it now takes 0x4015 rather than 0x15). An assertion was +added to catch the old usage. See Nes_Apu.h. + +- Replaced Blip_Buffer::buffer_size() and Blip_Buffer::units_per_sample() with +simpler functions (see Blip_Buffer.h and updated demo) + + // old way + if ( !buf.buffer_size( sample_count ) ) + out_of_memory(); + buf.units_per_sample( clocks_per_sec / samples_per_sec ); + + // new way + int length = 1000 / 2; // 1/2 second buffer length + if ( !buf.sample_rate( samples_per_sec, length ) ) + out_of_memory(); + buf.clock_rate( clocks_per_sec ); + +- Renamed Nes_Apu::master_volume() to volume() + +- Added Nes_Apu::output() to assign all oscillators in one call. + +- Changed Nes_Apu_Reflector.cpp to better insulate it: + + if ( load ) apu.noise.was_playing = true; // removed + if ( load ) apu.dmc.last_amp = apu.dmc.dac; // removed + if ( load ) apu.state_restored(); // added to end of reflect_apu() + +- Expanded notes + +- Updated to Blip_Buffer 0.3.0 + +- Improved demo code + +- Made assertions more descriptive when they fail + +- Fixed problem with Solstice and the noise oscillator + +- Added usage.txt with full examples of use in a NES emulator + +- Optimized Nes_Apu::earliest_irq() and irq_notifer() invocation + + +Nes_Snd_Emu 0.1.3 +----------------- +- Added and moved documentation. References are now in header files. + +- Nes_Apu has full support for frame and DMC IRQs. + +- Nes_Apu supports Blip_Buffer's improved treble equalization. + +- Added Nes_Apu_Reflector for saving and restoring the APU's sound state +to/from a file. Meant for emulators which freeze exact game state. + +- Reworked Nes_Snd_Emu's interface; the inherent fixed frame rate has been +eliminated. + +- Added NES_SND_EMU_QUALITY setting. + +- Deprecated Nes_Channel and nes_sample_t; use Blip_Buffer and blip_sample_t +instead. + + +Nes_Snd_Emu 0.1 +--------------- +- Numerous changes from previous releases. diff -bNaHudr bla1/blarg-sound/demo.cpp bla2/blarg-sound/demo.cpp --- bla1/blarg-sound/demo.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/demo.cpp 2005-10-03 15:03:20.000000000 +0300 @@ -0,0 +1,129 @@ + +// Use Simple_Apu to play random tones. Write output to sound file "out.wav". + +#include "Simple_Apu.h" + +#include + +// Uncomment to use SDL sound +//#include "SDL.h" + +const long sample_rate = 44100; +static Simple_Apu apu; + +// "emulate" 1/60 second of sound +static void emulate_frame() +{ + // Decay current tone + static int volume; + apu.write_register( 0x4000, 0xb0 | volume ); + volume--; + if ( volume < 0 ) + { + volume = 15; + + // Start a new random tone + apu.write_register( 0x4015, 0x01 ); + apu.write_register( 0x4002, rand() & 0xff ); + apu.write_register( 0x4003, (rand() & 3) | 0x11 ); + } + + // Generate 1/60 second of sound into APU's sample buffer + apu.end_frame(); +} + +static int read_dmc( void*, cpu_addr_t addr ) +{ + // call your memory read function here + //return read_memory( addr ); + return 0; +} + +static void init_sound(); +static void play_samples( const blip_sample_t*, long count ); +static void cleanup_sound(); + +int main( int argc, char** argv ) +{ + init_sound(); + + // Set sample rate and check for out of memory error + if ( apu.sample_rate( sample_rate ) ) + return EXIT_FAILURE; + + // Set function for APU to read memory with (required for DMC samples to play properly) + apu.dmc_reader( read_dmc, NULL ); + + // Generate a few seconds of sound + for ( int n = 60 * 4; n--; ) + { + // Simulate emulation of 1/60 second frame + emulate_frame(); + + // Samples from the frame can now be read out of the apu, or + // allowed to accumulate and read out later. Use samples_avail() + // to find out how many samples are currently in the buffer. + + int const buf_size = 2048; + static blip_sample_t buf [buf_size]; + + // Play whatever samples are available + long count = apu.read_samples( buf, buf_size ); + play_samples( buf, count ); + } + + cleanup_sound(); + + return 0; +} + + +// Sound output handling (either to SDL or wave file) + +#ifdef SDL_INIT_AUDIO + + #include "Sound_Queue.h" + + static Sound_Queue* sound_queue; + + static void init_sound() + { + if ( SDL_Init( SDL_INIT_AUDIO ) < 0 ) + exit( EXIT_FAILURE ); + + atexit( SDL_Quit ); + + sound_queue = new Sound_Queue; + if ( !sound_queue ) + exit( EXIT_FAILURE ); + + if ( sound_queue->init( sample_rate ) ) + exit( EXIT_FAILURE ); + } + + static void cleanup_sound() + { + delete sound_queue; + } + + static void play_samples( const blip_sample_t* samples, long count ) + { + sound_queue->write( samples, count ); + } + +#else + + #include "Wave_Writer.hpp" + + static void init_sound() { } + static void cleanup_sound() { } + + static void play_samples( const blip_sample_t* samples, long count ) + { + // write samples to sound file + static Wave_Writer wave( sample_rate ); + wave.write( samples, count ); + } + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/Blip_Buffer.cpp bla2/blarg-sound/nes_apu/Blip_Buffer.cpp --- bla1/blarg-sound/nes_apu/Blip_Buffer.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Blip_Buffer.cpp 2006-08-30 02:07:59.000000000 +0300 @@ -0,0 +1,408 @@ + +// Blip_Buffer 0.4.0. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" + +#include +#include +#include +#include +#include + +#define ULONGLONG_MAX 0xFFFFFFFFFFFFFFFFULL + +/* Copyright (C) 2003-2006 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 */ + +int const buffer_extra = blip_widest_impulse_ + 2; + +Blip_Buffer::Blip_Buffer() +{ + factor_ = LONG_MAX; + offset_ = 0; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + reader_accum = 0; + bass_shift = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + buf_t_ i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting to short truncates to 16 bits and sign-extends + i = 0x18000; + assert( (short) i == -0x8000 ); + #endif +} + +Blip_Buffer::~Blip_Buffer() +{ + free( buffer_ ); +} + +void Blip_Buffer::clear( int entire_buffer ) +{ + offset_ = 0; + reader_accum = 0; + if ( buffer_ ) + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + buffer_extra) * sizeof (buf_t_) ); + } +} + +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +{ + // start with maximum length that resampled time can represent + long long new_size = (ULONGLONG_MAX >> BLIP_BUFFER_ACCURACY) - buffer_extra - 64; + if ( msec != blip_max_length ) + { + long s = (new_rate * (msec + 1) + 999) / 1000; + if ( s < new_size ) + new_size = s; + else + assert( 0 ); // fails if requested buffer length exceeds limit + } + + if ( buffer_size_ != new_size ) + { + void* p = realloc( buffer_, (new_size + buffer_extra) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + buffer_ = (buf_t_*) p; + } + + buffer_size_ = new_size; + + // update things based on the sample rate + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( msec ) + assert( length_ == msec ); // ensure length is same as that passed in + if ( clock_rate_ != 0.0 ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return 0; // success +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( double clock_rate ) const +{ + double ratio = (double) sample_rate_ / clock_rate; + long long factor = (long long) floor( ratio * (1LL << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; +} + +void Blip_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + int shift = 31; + if ( freq > 0 ) + { + shift = 13; + long long f = ((long long)freq << 16) / sample_rate_; + while ( (f >>= 1) && --shift ) { } + } + bass_shift = shift; +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length +} + +void Blip_Buffer::remove_silence( long long count ) +{ + assert( count <= samples_avail() ); // tried to remove more samples than available + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; +} + +long long Blip_Buffer::count_samples( blip_time_t t ) const +{ + unsigned long long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + unsigned long long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (long long) (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( long long count ) const +{ + if ( count > buffer_size_ ) + count = buffer_size_; + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); +} + +void Blip_Buffer::remove_samples( long count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + long remain = samples_avail() + buffer_extra; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +// Blip_Synth_ + +Blip_Synth_::Blip_Synth_( short* p, int w ) : + impulses( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +static double const pi = 3.1415926535897932384626433832795029; + +static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) +{ + if ( cutoff >= 0.999 ) + cutoff = 0.999; + + if ( treble < -300.0 ) + treble = -300.0; + if ( treble > 5.0 ) + treble = 5.0; + + double const maxh = 4096.0; + double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); + double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); + double const to_angle = pi / 2 / maxh / oversample; + for ( int i = 0; i < count; i++ ) + { + double angle = ((i - count) * 2 + 1) * to_angle; + double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); + double cos_nc_angle = cos( maxh * cutoff * angle ); + double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); + double cos_angle = cos( angle ); + + c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; + double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); + double b = 2.0 - cos_angle - cos_angle; + double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; + + out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d + } +} + +void blip_eq_t::generate( float* out, int count ) const +{ + // lower cutoff freq for narrow kernels with their wider transition band + // (8 points->1.49, 16 points->1.15) + double oversample = blip_res * 2.25 / count + 0.85; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + oversample = half_rate / cutoff_freq; + double cutoff = rolloff_freq * oversample / half_rate; + + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); + + // apply (half of) hamming window + double to_fraction = pi / (count - 1); + for ( int i = count; i--; ) + out [i] *= 0.54 - 0.46 * cos( i * to_fraction ); +} + +void Blip_Synth_::adjust_impulse() +{ + // sum pairs for each phase and add error correction to end of first half + int const size = impulses_size(); + for ( int p = blip_res; p-- >= blip_res / 2; ) + { + int p2 = blip_res - 2 - p; + long error = kernel_unit; + for ( int i = 1; i < size; i += blip_res ) + { + error -= impulses [i + p ]; + error -= impulses [i + p2]; + } + if ( p == p2 ) + error /= 2; // phase = 0.5 impulse uses same half for both sides + impulses [size - blip_res + p] += error; + //printf( "error: %ld\n", error ); + } + + //for ( int i = blip_res; i--; printf( "\n" ) ) + // for ( int j = 0; j < width / 2; j++ ) + // printf( "%5ld,", impulses [j * blip_res + i + 1] ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; + + int const half_size = blip_res / 2 * (width - 1); + eq.generate( &fimpulse [blip_res], half_size ); + + int i; + + // need mirror slightly past center for calculation + for ( i = blip_res; i--; ) + fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; + + // starts at 0 + for ( i = 0; i < blip_res; i++ ) + fimpulse [i] = 0.0f; + + // find rescale factor + double total = 0.0; + for ( i = 0; i < half_size; i++ ) + total += fimpulse [blip_res + i]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / 2 / total; + kernel_unit = (long) base_unit; + + // integrate, first difference, rescale, convert to int + double sum = 0.0; + double next = 0.0; + int const impulses_size = this->impulses_size(); + for ( i = 0; i < impulses_size; i++ ) + { + impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); + sum += fimpulse [i]; + next += fimpulse [i + blip_res]; + } + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( new_unit != volume_unit_ ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + volume_unit_ = new_unit; + double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) + { + int shift = 0; + + // if unit is really small, might need to attenuate kernel + while ( factor < 2.0 ) + { + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + // keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values + long offset = 0x8000 + (1 << (shift - 1)); + long offset2 = 0x8000 >> shift; + for ( int i = impulses_size(); i--; ) + impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); + adjust_impulse(); + } + } + delta_factor = (int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); + } +} + +long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, int stereo ) +{ + long count = samples_avail(); + if ( count > max_samples ) + count = max_samples; + + if ( count ) + { + int const sample_shift = blip_sample_bits - 16; + int const bass_shift = this->bass_shift; + long accum = reader_accum; + buf_t_* in = buffer_; + + if ( !stereo ) + { + for ( long n = count; n--; ) + { + long s = accum >> sample_shift; + accum -= accum >> bass_shift; + accum += *in++; + *out++ = (blip_sample_t) s; + + // clamp sample + if ( (blip_sample_t) s != s ) + out [-1] = (blip_sample_t) (0x7FFF - (s >> 24)); + } + } + else + { + for ( long n = count; n--; ) + { + long s = accum >> sample_shift; + accum -= accum >> bass_shift; + accum += *in++; + *out = (blip_sample_t) s; + out += 2; + + // clamp sample + if ( (blip_sample_t) s != s ) + out [-2] = (blip_sample_t) (0x7FFF - (s >> 24)); + } + } + + reader_accum = accum; + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) +{ + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; + + int const sample_shift = blip_sample_bits - 16; + int prev = 0; + while ( count-- ) + { + long s = (long) *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + diff -bNaHudr bla1/blarg-sound/nes_apu/Blip_Buffer.h bla2/blarg-sound/nes_apu/Blip_Buffer.h --- bla1/blarg-sound/nes_apu/Blip_Buffer.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Blip_Buffer.h 2006-08-30 02:05:37.000000000 +0300 @@ -0,0 +1,354 @@ + +// Band-limited sound synthesis and buffering + +// Blip_Buffer 0.4.0 + +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + +// Time unit at source clock rate +typedef long long blip_time_t; + +// Output samples are 16-bit signed, with a range of -32768 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; + +class Blip_Buffer { +public: + typedef const char* blargg_err_t; + + // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there + // isn't enough memory, returns error without affecting current buffer setup. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); + + // Set number of source time units per second + void clock_rate( double ); + + // End current time frame of specified duration and make its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + + // Read at most 'max_samples' out of buffer into 'dest', removing them from from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments '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, int stereo = 0 ); + +// Additional optional features + + // Current output sample rate + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // Number of source time units per second + double clock_rate() const; + + // Set frequency high-pass filter frequency, where higher values reduce bass more + void bass_freq( int frequency ); + + // Number of samples delay from synthesis to samples read out + int output_latency() const; + + // Remove all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); + + // Number of samples available for reading with read_samples() + long samples_avail() const; + + // Remove 'count' samples from those waiting to be read + void remove_samples( long count ); + +// Experimental features + + // Number of raw samples that can be mixed within frame of specified duration. + long long count_samples( blip_time_t duration ) const; + + // Mix 'count' samples from 'buf' into buffer. + void mix_samples( blip_sample_t const* 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 becomes full. + blip_time_t count_clocks( long long count ) const; + + // not documented yet + typedef unsigned long long blip_resampled_time_t; + void remove_silence( long long count ); + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } + blip_resampled_time_t clock_rate_factor( double clock_rate ) const; +public: + Blip_Buffer(); + ~Blip_Buffer(); + + // Deprecated + typedef blip_resampled_time_t resampled_time_t; + blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } + blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } +private: + // noncopyable + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); +public: + typedef long buf_t_; + unsigned long long factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + long buffer_size_; +private: + long reader_accum; + int bass_shift; + long sample_rate_; + double clock_rate_; + int bass_freq_; + int length_; + friend class Blip_Reader; +}; + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +// Number of bits in resample ratio fraction. Higher values give a more accurate ratio +// but reduce maximum buffer size. +#ifndef BLIP_BUFFER_ACCURACY + #define BLIP_BUFFER_ACCURACY 29 +#endif + +// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in +// noticeable broadband noise when synthesizing high frequency square waves. +// Affects size of Blip_Synth objects since they store the waveform directly. +#ifndef BLIP_PHASE_BITS + #define BLIP_PHASE_BITS 7 +#endif + + // Internal + typedef unsigned long long blip_resampled_time_t; + long long const blip_widest_impulse_ = 16; + long long const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class Blip_Synth_ { + double volume_unit_; + short* const impulses; + int const width; + long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + void volume_unit( double ); + }; + +// Quality level. Start with blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class Blip_Synth { +public: + // Set overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configure low-pass filter (see notes.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Get/set Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Update amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Add an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( blip_time_t t, int delta ) const { + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); + } + +public: + Blip_Synth() : impl( impulses, quality ) { } +private: + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; + Blip_Synth_ impl; +}; + +// Low-pass equalization parameters +class blip_eq_t { +public: + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See notes.txt + blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); + +private: + double treble; + long rolloff_freq; + long sample_rate; + long cutoff_freq; + void generate( float* out, int count ) const; + friend class Blip_Synth_; +}; + +int const blip_sample_bits = 30; + +// Optimized inline sample reader for custom sample formats and mixing of Blip_Buffer samples +class Blip_Reader { +public: + // Begin reading samples from buffer. Returns value to pass to next() (can + // be ignored if default bass_freq is acceptable). + int begin( Blip_Buffer& ); + + // Current sample + long read() const { return accum >> (blip_sample_bits - 16); } + + // Current raw sample in full internal resolution + long read_raw() const { return accum; } + + // Advance to next sample + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + + // End reading samples from buffer. The number of samples read must now be removed + // using Blip_Buffer::remove_samples(). + void end( Blip_Buffer& b ) { b.reader_accum = accum; } + +private: + const Blip_Buffer::buf_t_* buf; + long accum; +}; + + +// End of public interface + + +#include + +// Compatibility with older version +const long blip_unscaled = 65535; +const int blip_low_quality = blip_med_quality; +const int blip_best_quality = blip_high_quality; + +#define BLIP_FWD( i ) { \ + long t0 = i0 * delta + buf [fwd + i]; \ + long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i]; \ + i0 = imp [blip_res * (i + 2)]; \ + buf [fwd + i] = t0; \ + buf [fwd + 1 + i] = t1; } + +#define BLIP_REV( r ) { \ + long t0 = i0 * delta + buf [rev - r]; \ + long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r]; \ + i0 = imp [blip_res * (r - 1)]; \ + buf [rev - r] = t0; \ + buf [rev + 1 - r] = t1; } + +template +inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the + // need for a longer buffer as set by set_sample_rate(). + assert( (long long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + delta *= impl.delta_factor; + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + imp_t const* imp = impulses + blip_res - phase; + long* buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + long i0 = *imp; + + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + int const mid = quality / 2 - 1; + long t0 = i0 * delta + buf [fwd + mid - 1]; + long t1 = imp [blip_res * mid] * delta + buf [fwd + mid]; + imp = impulses + phase; + i0 = imp [blip_res * mid]; + buf [fwd + mid - 1] = t0; + buf [fwd + mid] = t1; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + long t0 = i0 * delta + buf [rev]; + long t1 = *imp * delta + buf [rev + 1]; + buf [rev] = t0; + buf [rev + 1] = t1; +} + +#undef BLIP_FWD +#undef BLIP_REV + +template +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); +} + +template +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); +} + +inline blip_eq_t::blip_eq_t( double t ) : + treble( t ), rolloff_freq( 0 ), sample_rate( 48000 ), cutoff_freq( 0 ) { } +inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : + treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } + +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 sample_rate_; } +inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } +inline double Blip_Buffer::clock_rate() const { return clock_rate_; } +inline void Blip_Buffer::clock_rate( double cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } + +inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) +{ + buf = blip_buf.buffer_; + accum = blip_buf.reader_accum; + return blip_buf.bass_shift; +} + +int const blip_max_length = 0; +int const blip_default_length = 250; + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/Blip_Synth.h bla2/blarg-sound/nes_apu/Blip_Synth.h --- bla1/blarg-sound/nes_apu/Blip_Synth.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Blip_Synth.h 2005-08-29 22:02:24.000000000 +0300 @@ -0,0 +1,203 @@ + +// Blip_Synth and Blip_Wave are waveform transition synthesizers for adding +// waveforms to a Blip_Buffer. + +// Blip_Buffer 0.3.3. 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 +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; +public: + Blip_Synth() { impulse.init( impulses, width, res, fine_bits ); } + + // 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 +class Blip_Wave { + Blip_Synth synth; + blip_time_t time_; + int last_amp; +public: + // Start wave at time 0 and amplitude 0 + Blip_Wave() : time_( 0 ), last_amp( 0 ) { } + + // 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. + void end_frame( blip_time_t duration ) { + assert(( "Blip_Wave::end_frame(): Wave hadn't yet been run for entire frame", + duration <= time_ )); + time_ -= duration; + } +}; + + +// End of public interface + +template +void Blip_Wave::amplitude( int amp ) { + int delta = amp - last_amp; + last_amp = amp; + synth.offset_inline( time_, delta ); +} + +template +inline void Blip_Synth::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 +void Blip_Synth::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const { + offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); +} + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/Multi_Buffer.cpp bla2/blarg-sound/nes_apu/Multi_Buffer.cpp --- bla1/blarg-sound/nes_apu/Multi_Buffer.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Multi_Buffer.cpp 2006-08-17 20:55:35.000000000 +0300 @@ -0,0 +1,215 @@ + +// Blip_Buffer 0.4.0. http://www.slack.net/~ant/ + +#include "Multi_Buffer.h" + +/* Copyright (C) 2003-2006 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 ) +{ + 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] ); +} + diff -bNaHudr bla1/blarg-sound/nes_apu/Multi_Buffer.h bla2/blarg-sound/nes_apu/Multi_Buffer.h --- bla1/blarg-sound/nes_apu/Multi_Buffer.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Multi_Buffer.h 2006-08-17 20:55:35.000000000 +0300 @@ -0,0 +1,175 @@ + +// Multi-channel sound buffer interface, and basic mono and stereo buffers + +// Blip_Buffer 0.4.0 + +#ifndef MULTI_BUFFER_H +#define MULTI_BUFFER_H + +#include "blargg_common.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 Multi_Buffer::set_sample_rate( long rate, int msec ) +{ + sample_rate_ = rate; + length_ = msec; + return blargg_success; +} + +inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec ) +{ + return Multi_Buffer::set_sample_rate( rate, msec ); +} + +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 ) { 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 + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Apu.cpp bla2/blarg-sound/nes_apu/Nes_Apu.cpp --- bla1/blarg-sound/nes_apu/Nes_Apu.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Apu.cpp 2006-08-17 20:57:43.000000000 +0300 @@ -0,0 +1,383 @@ + +// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ + +#include "Nes_Apu.h" + +/* Copyright (C) 2003-2006 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 + +int const amp_range = 15; + +Nes_Apu::Nes_Apu() : + square1( &square_synth ), + square2( &square_synth ) +{ + dmc.apu = this; + dmc.rom_reader = NULL; + irq_notifier_ = NULL; + + oscs [0] = &square1; + oscs [1] = &square2; + oscs [2] = ▵ + oscs [3] = &noise; + oscs [4] = &dmc; + + output( NULL ); + volume( 1.0 ); + reset( false ); +} + +Nes_Apu::~Nes_Apu() +{ +} + +void Nes_Apu::treble_eq( const blip_eq_t& eq ) +{ + square_synth.treble_eq( eq ); + triangle.synth.treble_eq( eq ); + noise.synth.treble_eq( eq ); + dmc.synth.treble_eq( eq ); +} + +void Nes_Apu::enable_nonlinear( double v ) +{ + dmc.nonlinear = true; + square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); + + const double tnd = 0.48 / 202 * nonlinear_tnd_gain(); + triangle.synth.volume( 3.0 * tnd ); + noise.synth.volume( 2.0 * tnd ); + dmc.synth.volume( tnd ); + + square1 .last_amp = 0; + square2 .last_amp = 0; + triangle.last_amp = 0; + noise .last_amp = 0; + dmc .last_amp = 0; +} + +void Nes_Apu::volume( double v ) +{ + dmc.nonlinear = false; + square_synth.volume( 0.1128 / amp_range * v ); + triangle.synth.volume( 0.12765 / amp_range * v ); + noise.synth.volume( 0.0741 / amp_range * v ); + dmc.synth.volume( 0.42545 / 127 * v ); +} + +void Nes_Apu::output( Blip_Buffer* buffer ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buffer ); +} + +void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) +{ + // to do: time pal frame periods exactly + frame_period = pal_mode ? 8314 : 7458; + dmc.pal_mode = pal_mode; + + square1.reset(); + square2.reset(); + triangle.reset(); + noise.reset(); + dmc.reset(); + + last_time = 0; + last_dmc_time = 0; + osc_enables = 0; + irq_flag = false; + earliest_irq_ = no_irq; + frame_delay = 1; + write_register( 0, 0x4017, 0x00 ); + write_register( 0, 0x4015, 0x00 ); + + for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ ) + write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); + + dmc.dac = initial_dmc_dac; + //if ( !dmc.nonlinear ) // to do: remove? + // dmc.last_amp = initial_dmc_dac; // prevent output transition +} + +void Nes_Apu::irq_changed() +{ + nes_time_t new_irq = dmc.next_irq; + if ( dmc.irq_flag | irq_flag ) { + new_irq = 0; + } + else if ( new_irq > next_irq ) { + new_irq = next_irq; + } + + if ( new_irq != earliest_irq_ ) { + earliest_irq_ = new_irq; + if ( irq_notifier_ ) + irq_notifier_( irq_data ); + } +} + +// frames + +void Nes_Apu::run_until( nes_time_t end_time ) +{ + require( end_time >= last_dmc_time ); + if ( end_time > next_dmc_read_time() ) + { + nes_time_t start = last_dmc_time; + last_dmc_time = end_time; + dmc.run( start, end_time ); + } +} + +void Nes_Apu::run_until_( nes_time_t end_time ) +{ + require( end_time >= last_time ); + + if ( end_time == last_time ) + return; + + if ( last_dmc_time < end_time ) + { + nes_time_t start = last_dmc_time; + last_dmc_time = end_time; + dmc.run( start, end_time ); + } + + while ( true ) + { + // earlier of next frame time or end time + nes_time_t time = last_time + frame_delay; + if ( time > end_time ) + time = end_time; + frame_delay -= time - last_time; + + // run oscs to present + square1.run( last_time, time ); + square2.run( last_time, time ); + triangle.run( last_time, time ); + noise.run( last_time, time ); + last_time = time; + + if ( time == end_time ) + break; // no more frames to run + + // take frame-specific actions + frame_delay = frame_period; + switch ( frame++ ) + { + case 0: + if ( !(frame_mode & 0xc0) ) { + next_irq = time + frame_period * 4 + 1; + irq_flag = true; + } + // fall through + case 2: + // clock length and sweep on frames 0 and 2 + square1.clock_length( 0x20 ); + square2.clock_length( 0x20 ); + noise.clock_length( 0x20 ); + triangle.clock_length( 0x80 ); // different bit for halt flag on triangle + + square1.clock_sweep( -1 ); + square2.clock_sweep( 0 ); + break; + + case 1: + // frame 1 is slightly shorter + frame_delay -= 2; + break; + + case 3: + frame = 0; + + // frame 3 is almost twice as long in mode 1 + if ( frame_mode & 0x80 ) + frame_delay += frame_period - 6; + break; + } + + // clock envelopes and linear counter every frame + triangle.clock_linear_counter(); + square1.clock_envelope(); + square2.clock_envelope(); + noise.clock_envelope(); + } +} + +// to do: remove +static long abs_time; + +template +inline void zero_apu_osc( T* osc, nes_time_t time ) +{ + Blip_Buffer* output = osc->output; + int last_amp = osc->last_amp; + osc->last_amp = 0; + if ( output && last_amp ) + osc->synth.offset( time, -last_amp, output ); +} + +void Nes_Apu::end_frame( nes_time_t end_time ) +{ + if ( end_time > last_time ) + run_until_( end_time ); + + abs_time += end_time; + + if ( dmc.nonlinear ) + { + zero_apu_osc( &square1, last_time ); + zero_apu_osc( &square2, last_time ); + zero_apu_osc( &triangle, last_time ); + zero_apu_osc( &noise, last_time ); + zero_apu_osc( &dmc, last_time ); + } + + // make times relative to new frame + last_time -= end_time; + require( last_time >= 0 ); + + last_dmc_time -= end_time; + require( last_dmc_time >= 0 ); + + if ( next_irq != no_irq ) { + next_irq -= end_time; + assert( next_irq >= 0 ); + } + if ( dmc.next_irq != no_irq ) { + dmc.next_irq -= end_time; + assert( dmc.next_irq >= 0 ); + } + if ( earliest_irq_ != no_irq ) { + earliest_irq_ -= end_time; + if ( earliest_irq_ < 0 ) + earliest_irq_ = 0; + } +} + +// registers + +static const unsigned char length_table [0x20] = { + 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, + 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, + 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, + 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E +}; + +void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) +{ + require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx) + require( (unsigned) data <= 0xff ); + + // Ignore addresses outside range + if ( addr < start_addr || end_addr < addr ) + return; + + run_until_( time ); + + if ( addr < 0x4014 ) + { + // Write to channel + int osc_index = (addr - start_addr) >> 2; + Nes_Osc* osc = oscs [osc_index]; + + int reg = addr & 3; + osc->regs [reg] = data; + osc->reg_written [reg] = true; + + if ( osc_index == 4 ) + { + // handle DMC specially + dmc.write_register( reg, data ); + } + else if ( reg == 3 ) + { + // load length counter + if ( (osc_enables >> osc_index) & 1 ) + osc->length_counter = length_table [(data >> 3) & 0x1f]; + + // reset square phase + if ( osc_index < 2 ) + ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1; + } + } + else if ( addr == 0x4015 ) + { + // Channel enables + for ( int i = osc_count; i--; ) + if ( !((data >> i) & 1) ) + oscs [i]->length_counter = 0; + + bool recalc_irq = dmc.irq_flag; + dmc.irq_flag = false; + + int old_enables = osc_enables; + osc_enables = data; + if ( !(data & 0x10) ) { + dmc.next_irq = no_irq; + recalc_irq = true; + } + else if ( !(old_enables & 0x10) ) { + dmc.start(); // dmc just enabled + } + + if ( recalc_irq ) + irq_changed(); + } + else if ( addr == 0x4017 ) + { + // Frame mode + frame_mode = data; + + bool irq_enabled = !(data & 0x40); + irq_flag &= irq_enabled; + next_irq = no_irq; + + // mode 1 + frame_delay = (frame_delay & 1); + frame = 0; + + if ( !(data & 0x80) ) + { + // mode 0 + frame = 1; + frame_delay += frame_period; + if ( irq_enabled ) + next_irq = time + frame_delay + frame_period * 3; + } + + irq_changed(); + } +} + +int Nes_Apu::read_status( nes_time_t time ) +{ + run_until_( time - 1 ); + + int result = (dmc.irq_flag << 7) | (irq_flag << 6); + + for ( int i = 0; i < osc_count; i++ ) + if ( oscs [i]->length_counter ) + result |= 1 << i; + + run_until_( time ); + + if ( irq_flag ) { + irq_flag = false; + irq_changed(); + } + + return result; +} + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Apu.h bla2/blarg-sound/nes_apu/Nes_Apu.h --- bla1/blarg-sound/nes_apu/Nes_Apu.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Apu.h 2006-08-17 20:57:43.000000000 +0300 @@ -0,0 +1,174 @@ + +// NES 2A03 APU sound chip emulator + +// Nes_Snd_Emu 0.1.7 + +#ifndef NES_APU_H +#define NES_APU_H + +typedef long nes_time_t; // CPU clock cycle count +typedef unsigned nes_addr_t; // 16-bit memory address + +#include "Nes_Oscs.h" + +struct apu_snapshot_t; +class Nonlinear_Buffer; + +class Nes_Apu { +public: + Nes_Apu(); + ~Nes_Apu(); + + // Set buffer to generate all sound into, or disable sound if NULL + void output( Blip_Buffer* ); + + // Set memory reader callback used by DMC oscillator to fetch samples. + // When callback is invoked, 'user_data' is passed unchanged as the + // first parameter. + void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL ); + + // All time values are the number of CPU clock cycles relative to the + // beginning of the current time frame. Before resetting the CPU clock + // count, call end_frame( last_cpu_time ). + + // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) + enum { start_addr = 0x4000 }; + enum { end_addr = 0x4017 }; + void write_register( nes_time_t, nes_addr_t, int data ); + + // Read from status register at 0x4015 + enum { status_addr = 0x4015 }; + int read_status( nes_time_t ); + + // Run all oscillators up to specified time, end current time frame, then + // start a new time frame at time 0. Time frames have no effect on emulation + // and each can be whatever length is convenient. + void end_frame( nes_time_t ); + +// Additional optional features (can be ignored without any problem) + + // Reset internal frame counter, registers, and all oscillators. + // Use PAL timing if pal_timing is true, otherwise use NTSC timing. + // Set the DMC oscillator's initial DAC value to initial_dmc_dac without + // any audible click. + void reset( bool pal_timing = false, int initial_dmc_dac = 0 ); + + // Save/load snapshot of exact emulation state + void save_snapshot( apu_snapshot_t* out ) const; + void load_snapshot( apu_snapshot_t const& ); + + // Set overall volume (default is 1.0) + void volume( double ); + + // Set treble equalization (see notes.txt) + void treble_eq( const blip_eq_t& ); + + // Set sound output of specific oscillator to buffer. If buffer is NULL, + // the specified oscillator is muted and emulation accuracy is reduced. + // The oscillators are indexed as follows: 0) Square 1, 1) Square 2, + // 2) Triangle, 3) Noise, 4) DMC. + enum { osc_count = 5 }; + void osc_output( int index, Blip_Buffer* buffer ); + + // Set IRQ time callback that is invoked when the time of earliest IRQ + // may have changed, or NULL to disable. When callback is invoked, + // 'user_data' is passed unchanged as the first parameter. + void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); + + // Get time that APU-generated IRQ will occur if no further register reads + // or writes occur. If IRQ is already pending, returns irq_waiting. If no + // IRQ will occur, returns no_irq. + enum { no_irq = LONG_MAX / 2 + 1 }; + enum { irq_waiting = 0 }; + nes_time_t earliest_irq( nes_time_t ) const; + + // Count number of DMC reads that would occur if 'run_until( t )' were executed. + // If last_read is not NULL, set *last_read to the earliest time that + // 'count_dmc_reads( time )' would result in the same result. + int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const; + + // Time when next DMC memory read will occur + nes_time_t next_dmc_read_time() const; + + // Run DMC until specified time, so that any DMC memory reads can be + // accounted for (i.e. inserting CPU wait states). + void run_until( nes_time_t ); + +// End of public interface. +private: + friend class Nes_Nonlinearizer; + void enable_nonlinear( double volume ); + static double nonlinear_tnd_gain() { return 0.75; } +private: + friend struct Nes_Dmc; + + // noncopyable + Nes_Apu( const Nes_Apu& ); + Nes_Apu& operator = ( const Nes_Apu& ); + + Nes_Osc* oscs [osc_count]; + Nes_Square square1; + Nes_Square square2; + Nes_Noise noise; + Nes_Triangle triangle; + Nes_Dmc dmc; + + nes_time_t last_time; // has been run until this time in current frame + nes_time_t last_dmc_time; + nes_time_t earliest_irq_; + nes_time_t next_irq; + int frame_period; + int frame_delay; // cycles until frame counter runs next + int frame; // current frame (0-3) + int osc_enables; + int frame_mode; + bool irq_flag; + void (*irq_notifier_)( void* user_data ); + void* irq_data; + Nes_Square::Synth square_synth; // shared by squares + + void irq_changed(); + void state_restored(); + void run_until_( nes_time_t ); +}; + +inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf ) +{ + assert( (unsigned) osc < osc_count ); + oscs [osc]->output = buf; +} + +inline nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const +{ + return earliest_irq_; +} + +inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data ) +{ + dmc.rom_reader_data = user_data; + dmc.rom_reader = func; +} + +inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) +{ + irq_notifier_ = func; + irq_data = user_data; +} + +inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const +{ + return dmc.count_reads( time, last_read ); +} + +inline nes_time_t Nes_Dmc::next_read_time() const +{ + if ( length_counter == 0 ) + return Nes_Apu::no_irq; // not reading + + return apu->last_dmc_time + delay + long (bits_remain - 1) * period; +} + +inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Cpu.cpp bla2/blarg-sound/nes_apu/Nes_Cpu.cpp --- bla1/blarg-sound/nes_apu/Nes_Cpu.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Cpu.cpp 2006-08-17 20:57:43.000000000 +0300 @@ -0,0 +1,950 @@ + +// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/nes-emu/ + +#include "Nes_Cpu.h" + +#include +#include + +#include "blargg_endian.h" + +/* Copyright (C) 2003-2006 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 + +#if BLARGG_NONPORTABLE + #define PAGE_OFFSET( addr ) (addr) +#else + #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) +#endif + +Nes_Cpu::Nes_Cpu() +{ + callback_data = NULL; + reset(); +} + +inline void Nes_Cpu::set_code_page( int i, uint8_t const* p ) +{ + code_map [i] = p - PAGE_OFFSET( i * page_size ); +} + +void Nes_Cpu::reset( const void* unmapped_code_page, reader_t read, writer_t write ) +{ + r.status = 0; + r.sp = 0; + r.pc = 0; + r.a = 0; + r.x = 0; + r.y = 0; + + clock_count = 0; + base_time = 0; + clock_limit = 0; + irq_time_ = LONG_MAX / 2 + 1; + end_time_ = LONG_MAX / 2 + 1; + + for ( int i = 0; i < page_count + 1; i++ ) + { + set_code_page( i, (uint8_t*) unmapped_code_page ); + data_reader [i] = read; + data_writer [i] = write; + } +} + +void Nes_Cpu::map_code( nes_addr_t start, unsigned long size, const void* data ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= 0x10000 ); + + unsigned first_page = start / page_size; + for ( unsigned i = size / page_size; i--; ) + set_code_page( first_page + i, (uint8_t*) data + i * page_size ); +} + +void Nes_Cpu::set_reader( nes_addr_t start, unsigned long size, reader_t func ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= 0x10000 + page_size ); + + unsigned first_page = start / page_size; + for ( unsigned i = size / page_size; i--; ) + data_reader [first_page + i] = func; +} + +void Nes_Cpu::set_writer( nes_addr_t start, unsigned long size, writer_t func ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= 0x10000 + page_size ); + + unsigned first_page = start / page_size; + for ( unsigned i = size / page_size; i--; ) + data_writer [first_page + i] = func; +} + +// Note: 'addr' is evaulated more than once in the following macros, so it +// must not contain side-effects. + +#define READ( addr ) (data_reader [(addr) >> page_bits]( callback_data, addr )) +#define WRITE( addr, data ) (data_writer [(addr) >> page_bits]( callback_data, addr, data )) + +#define READ_LOW( addr ) (low_mem [int (addr)]) +#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) + +#define READ_PROG( addr ) (code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )]) +#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) ) + +#define SET_SP( v ) (sp = ((v) + 1) | 0x100) +#define GET_SP() ((sp - 1) & 0xff) + +#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) + +int Nes_Cpu::read( nes_addr_t addr ) +{ + return READ( addr ); +} + +void Nes_Cpu::write( nes_addr_t addr, int value ) +{ + WRITE( addr, value ); +} + +#ifndef NES_CPU_GLUE_ONLY + +static const unsigned char clock_table [256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 + 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 + 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 + 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 + 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A + 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D + 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,// E + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F +}; + +#include BLARGG_ENABLE_OPTIMIZER + +Nes_Cpu::result_t Nes_Cpu::run( nes_time_t end ) +{ + set_end_time( end ); + + volatile result_t result = result_cycles; + +#if BLARGG_CPU_POWERPC + // cache commonly-used values in registers + long clock_count = this->clock_count; + writer_t* const data_writer = this->data_writer; + reader_t* const data_reader = this->data_reader; + uint8_t* const low_mem = this->low_mem; +#endif + + // registers + unsigned pc = r.pc; + int sp; + SET_SP( r.sp ); + int a = r.a; + int x = r.x; + int y = r.y; + + // status flags + + const int st_n = 0x80; + const int st_v = 0x40; + const int st_r = 0x20; + const int st_b = 0x10; + const int st_d = 0x08; + const int st_i = 0x04; + const int st_z = 0x02; + const int st_c = 0x01; + + #define IS_NEG (nz & 0x880) + + #define CALC_STATUS( out ) do { \ + out = status & (st_v | st_d | st_i); \ + out |= (c >> 8) & st_c; \ + if ( IS_NEG ) out |= st_n; \ + if ( !(nz & 0xFF) ) out |= st_z; \ + } while ( false ) + + #define SET_STATUS( in ) do { \ + status = in & (st_v | st_d | st_i); \ + c = in << 8; \ + nz = (in << 4) & 0x800; \ + nz |= ~in & st_z; \ + } while ( false ) + + int status; + int c; // carry set if (c & 0x100) != 0 + int nz; // Z set if (nz & 0xff) == 0, N set if (nz & 0x880) != 0 + { + int temp = r.status; + SET_STATUS( temp ); + } + + goto loop; +dec_clock_loop: + clock_count--; +loop: + + assert( unsigned (GET_SP()) < 0x100 ); + assert( unsigned (a) < 0x100 ); + assert( unsigned (x) < 0x100 ); + assert( unsigned (y) < 0x100 ); + + uint8_t const* page = code_map [pc >> page_bits]; + unsigned opcode = page [PAGE_OFFSET( pc )]; + unsigned data = page [PAGE_OFFSET( pc ) + 1]; + pc++; + + // page crossing + //check( opcode == 0x60 || &READ_PROG( pc ) == &page [PAGE_OFFSET( pc )] ); + + if ( clock_count >= clock_limit ) + goto stop; + + clock_count += clock_table [opcode]; + + #if BLARGG_CPU_POWERPC + this->clock_count = clock_count; + #endif + + switch ( opcode ) + { + +// Macros + +#define ADD_PAGE (pc++, data += 0x100 * READ_PROG( pc )); +#define GET_ADDR() READ_PROG16( pc ) + +#define HANDLE_PAGE_CROSSING( lsb ) clock_count += (lsb) >> 8; + +#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; + +#define IND_Y { \ + int temp = READ_LOW( data ) + y; \ + data = temp + 0x100 * READ_LOW( uint8_t (data + 1) ); \ + HANDLE_PAGE_CROSSING( temp ); \ + } + +#define IND_X { \ + int temp = data + x; \ + data = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) ); \ + } + +#define ARITH_ADDR_MODES( op ) \ +case op - 0x04: /* (ind,x) */ \ + IND_X \ + goto ptr##op; \ +case op + 0x0C: /* (ind),y */ \ + IND_Y \ + goto ptr##op; \ +case op + 0x10: /* zp,X */ \ + data = uint8_t (data + x); \ +case op + 0x00: /* zp */ \ + data = READ_LOW( data ); \ + goto imm##op; \ +case op + 0x14: /* abs,Y */ \ + data += y; \ + goto ind##op; \ +case op + 0x18: /* abs,X */ \ + data += x; \ +ind##op: \ + HANDLE_PAGE_CROSSING( data ); \ +case op + 0x08: /* abs */ \ + ADD_PAGE \ +ptr##op: \ + data = READ( data ); \ +case op + 0x04: /* imm */ \ +imm##op: \ + +#define BRANCH( cond ) \ +{ \ + pc++; \ + int offset = (BOOST::int8_t) data; \ + int extra_clock = (pc & 0xff) + offset; \ + if ( !(cond) ) goto dec_clock_loop; \ + pc += offset; \ + clock_count += (extra_clock >> 8) & 1; \ + goto loop; \ +} + +// Often-Used + + case 0xB5: // LDA zp,x + data = uint8_t (data + x); + case 0xA5: // LDA zp + a = nz = READ_LOW( data ); + pc++; + goto loop; + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ); + + case 0x20: { // JSR + int temp = pc + 1; + pc = READ_PROG16( pc ); + WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); + sp = (sp - 2) | 0x100; + WRITE_LOW( sp, temp ); + goto loop; + } + + case 0x4C: // JMP abs + pc = READ_PROG16( pc ); + goto loop; + + case 0xE8: INC_DEC_XY( x, 1 ) // INX + + case 0x10: // BPL + BRANCH( !IS_NEG ) + + ARITH_ADDR_MODES( 0xC5 ) // CMP + nz = a - data; + pc++; + c = ~nz; + nz &= 0xff; + goto loop; + + case 0x30: // BMI + BRANCH( IS_NEG ) + + case 0xF0: // BEQ + BRANCH( !(uint8_t) nz ); + + case 0x95: // STA zp,x + data = uint8_t (data + x); + case 0x85: // STA zp + pc++; + WRITE_LOW( data, a ); + goto loop; + + case 0xC8: INC_DEC_XY( y, 1 ) // INY + + case 0xA8: // TAY + y = a; + case 0x98: // TYA + a = nz = y; + goto loop; + + case 0xB9: // LDA abs,Y + data += y; + goto lda_ind_common; + + case 0xBD: // LDA abs,X + data += x; + lda_ind_common: + HANDLE_PAGE_CROSSING( data ); + case 0xAD: // LDA abs + ADD_PAGE + lda_ptr: + a = nz = READ( data ); + pc++; + goto loop; + + case 0x60: // RTS + pc = 1 + READ_LOW( sp ); + pc += READ_LOW( 0x100 | (sp - 0xff) ) * 0x100; + sp = (sp - 0xfe) | 0x100; + goto loop; + + case 0x99: // STA abs,Y + data += y; + goto sta_ind_common; + + case 0x9D: // STA abs,X + data += x; + sta_ind_common: + HANDLE_PAGE_CROSSING( data ); + case 0x8D: // STA abs + ADD_PAGE + sta_ptr: + pc++; + WRITE( data, a ); + goto loop; + + case 0xA9: // LDA #imm + pc++; + a = data; + nz = data; + goto loop; + +// Branch + + case 0x50: // BVC + BRANCH( !(status & st_v) ) + + case 0x70: // BVS + BRANCH( status & st_v ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + +// Load/store + + case 0x94: // STY zp,x + data = uint8_t (data + x); + case 0x84: // STY zp + pc++; + WRITE_LOW( data, y ); + goto loop; + + case 0x96: // STX zp,y + data = uint8_t (data + y); + case 0x86: // STX zp + pc++; + WRITE_LOW( data, x ); + goto loop; + + case 0xB6: // LDX zp,y + data = uint8_t (data + y); + case 0xA6: // LDX zp + data = READ_LOW( data ); + case 0xA2: // LDX #imm + pc++; + x = data; + nz = data; + goto loop; + + case 0xB4: // LDY zp,x + data = uint8_t (data + x); + case 0xA4: // LDY zp + data = READ_LOW( data ); + case 0xA0: // LDY #imm + pc++; + y = data; + nz = data; + goto loop; + + case 0xB1: // LDA (ind),Y + IND_Y + goto lda_ptr; + + case 0xA1: // LDA (ind,X) + IND_X + goto lda_ptr; + + case 0x91: // STA (ind),Y + IND_Y + goto sta_ptr; + + case 0x81: // STA (ind,X) + IND_X + goto sta_ptr; + + case 0xBC: // LDY abs,X + data += x; + HANDLE_PAGE_CROSSING( data ); + case 0xAC:{// LDY abs + pc++; + unsigned addr = data + 0x100 * READ_PROG( pc ); + pc++; + y = nz = READ( addr ); + goto loop; + } + + case 0xBE: // LDX abs,y + data += y; + HANDLE_PAGE_CROSSING( data ); + case 0xAE:{// LDX abs + pc++; + unsigned addr = data + 0x100 * READ_PROG( pc ); + pc++; + x = nz = READ( addr ); + goto loop; + } + + { + int temp; + case 0x8C: // STY abs + temp = y; + goto store_abs; + + case 0x8E: // STX abs + temp = x; + store_abs: + unsigned addr = GET_ADDR(); + WRITE( addr, temp ); + pc += 2; + goto loop; + } + +// Compare + + case 0xEC:{// CPX abs + unsigned addr = GET_ADDR(); + pc++; + data = READ( addr ); + goto cpx_data; + } + + case 0xE4: // CPX zp + data = READ_LOW( data ); + case 0xE0: // CPX #imm + cpx_data: + nz = x - data; + pc++; + c = ~nz; + nz &= 0xff; + goto loop; + + case 0xCC:{// CPY abs + unsigned addr = GET_ADDR(); + pc++; + data = READ( addr ); + goto cpy_data; + } + + case 0xC4: // CPY zp + data = READ_LOW( data ); + case 0xC0: // CPY #imm + cpy_data: + nz = y - data; + pc++; + c = ~nz; + nz &= 0xff; + goto loop; + +// Logical + + ARITH_ADDR_MODES( 0x25 ) // AND + nz = (a &= data); + pc++; + goto loop; + + ARITH_ADDR_MODES( 0x45 ) // EOR + nz = (a ^= data); + pc++; + goto loop; + + ARITH_ADDR_MODES( 0x05 ) // ORA + nz = (a |= data); + pc++; + goto loop; + + case 0x2C:{// BIT abs + unsigned addr = GET_ADDR(); + pc++; + nz = READ( addr ); + goto bit_common; + } + + case 0x24: // BIT zp + nz = READ_LOW( data ); + bit_common: + pc++; + status &= ~st_v; + status |= nz & st_v; + // if result is zero, might also be negative, so use secondary N bit + if ( !(a & nz) ) + nz = (nz << 4) & 0x800; + goto loop; + +// Add/subtract + + ARITH_ADDR_MODES( 0xE5 ) // SBC + case 0xEB: // unofficial equivalent + data ^= 0xFF; + goto adc_imm; + + ARITH_ADDR_MODES( 0x65 ) // ADC + adc_imm: { + int carry = (c >> 8) & 1; + int ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend + status &= ~st_v; + status |= (ov >> 2) & 0x40; + c = nz = a + data + carry; + pc++; + a = (uint8_t) nz; + goto loop; + } + +// Shift/rotate + + case 0x4A: // LSR A + c = 0; + case 0x6A: // ROR A + nz = (c >> 1) & 0x80; // could use bit insert macro here + c = a << 8; + nz |= a >> 1; + a = nz; + goto loop; + + case 0x0A: // ASL A + nz = a << 1; + c = nz; + a = (uint8_t) nz; + goto loop; + + case 0x2A: { // ROL A + nz = a << 1; + int temp = (c >> 8) & 1; + c = nz; + nz |= temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x3E: // ROL abs,X + data += x; + goto rol_abs; + + case 0x1E: // ASL abs,X + data += x; + case 0x0E: // ASL abs + c = 0; + case 0x2E: // ROL abs + rol_abs: + HANDLE_PAGE_CROSSING( data ); + ADD_PAGE + nz = (c >> 8) & 1; + nz |= (c = READ( data ) << 1); + rotate_common: + pc++; + WRITE( data, (uint8_t) nz ); + goto loop; + + case 0x7E: // ROR abs,X + data += x; + goto ror_abs; + + case 0x5E: // LSR abs,X + data += x; + case 0x4E: // LSR abs + c = 0; + case 0x6E: // ROR abs + ror_abs: { + HANDLE_PAGE_CROSSING( data ); + ADD_PAGE + int temp = READ( data ); + nz = ((c >> 1) & 0x80) | (temp >> 1); + c = temp << 8; + goto rotate_common; + } + + case 0x76: // ROR zp,x + data = uint8_t (data + x); + goto ror_zp; + + case 0x56: // LSR zp,x + data = uint8_t (data + x); + case 0x46: // LSR zp + c = 0; + case 0x66: // ROR zp + ror_zp: { + int temp = READ_LOW( data ); + nz = ((c >> 1) & 0x80) | (temp >> 1); + c = temp << 8; + goto write_nz_zp; + } + + case 0x36: // ROL zp,x + data = uint8_t (data + x); + goto rol_zp; + + case 0x16: // ASL zp,x + data = uint8_t (data + x); + case 0x06: // ASL zp + c = 0; + case 0x26: // ROL zp + rol_zp: + nz = (c >> 8) & 1; + nz |= (c = READ_LOW( data ) << 1); + goto write_nz_zp; + +// Increment/decrement + + case 0xCA: INC_DEC_XY( x, -1 ) // DEX + + case 0x88: INC_DEC_XY( y, -1 ) // DEY + + case 0xF6: // INC zp,x + data = uint8_t (data + x); + case 0xE6: // INC zp + nz = 1; + goto add_nz_zp; + + case 0xD6: // DEC zp,x + data = uint8_t (data + x); + case 0xC6: // DEC zp + nz = -1; + add_nz_zp: + nz += READ_LOW( data ); + write_nz_zp: + pc++; + WRITE_LOW( data, nz ); + goto loop; + + case 0xFE: // INC abs,x + HANDLE_PAGE_CROSSING( data + x ); + data = x + GET_ADDR(); + goto inc_ptr; + + case 0xEE: // INC abs + data = GET_ADDR(); + inc_ptr: + nz = 1; + goto inc_common; + + case 0xDE: // DEC abs,x + HANDLE_PAGE_CROSSING( data + x ); + data = x + GET_ADDR(); + goto dec_ptr; + + case 0xCE: // DEC abs + data = GET_ADDR(); + dec_ptr: + nz = -1; + inc_common: + nz += READ( data ); + pc += 2; + WRITE( data, (uint8_t) nz ); + goto loop; + +// Transfer + + case 0xAA: // TAX + x = a; + case 0x8A: // TXA + a = nz = x; + goto loop; + + case 0x9A: // TXS + SET_SP( x ); // verified (no flag change) + goto loop; + + case 0xBA: // TSX + x = nz = GET_SP(); + goto loop; + +// Stack + + case 0x48: // PHA + PUSH( a ); // verified + goto loop; + + case 0x68: // PLA + a = nz = READ_LOW( sp ); + sp = (sp - 0xff) | 0x100; + goto loop; + + case 0x40: // RTI + { + int temp = READ_LOW( sp ); + pc = READ_LOW( 0x100 | (sp - 0xff) ); + pc |= READ_LOW( 0x100 | (sp - 0xfe) ) * 0x100; + sp = (sp - 0xfd) | 0x100; + data = status; + SET_STATUS( temp ); + } + if ( !((data ^ status) & st_i) ) + goto loop; // I flag didn't change + i_flag_changed: + //dprintf( "%6d %s\n", time(), (status & st_i ? "SEI" : "CLI") ); + this->r.status = status; // update externally-visible I flag + // update clock_limit based on modified I flag + clock_limit = end_time_; + if ( end_time_ <= irq_time_ ) + goto loop; + if ( status & st_i ) + goto loop; + clock_limit = irq_time_; + goto loop; + + case 0x28:{// PLP + int temp = READ_LOW( sp ); + sp = (sp - 0xff) | 0x100; + data = status; + SET_STATUS( temp ); + if ( !((data ^ status) & st_i) ) + goto loop; // I flag didn't change + if ( !(status & st_i) ) + goto handle_cli; + goto handle_sei; + } + + case 0x08: { // PHP + int temp; + CALC_STATUS( temp ); + PUSH( temp | st_b | st_r ); + goto loop; + } + + case 0x6C: // JMP (ind) + data = GET_ADDR(); + pc = READ( data ); + pc |= READ( (data & 0xff00) | ((data + 1) & 0xff) ) << 8; + goto loop; + + case 0x00: { // BRK + pc++; + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); + WRITE_LOW( 0x100 | (sp - 2), pc ); + int temp; + CALC_STATUS( temp ); + sp = (sp - 3) | 0x100; + WRITE_LOW( sp, temp | st_b | st_r ); + pc = READ_PROG16( 0xFFFE ); + status |= st_i; + goto i_flag_changed; + } + +// Flags + + case 0x38: // SEC + c = ~0; + goto loop; + + case 0x18: // CLC + c = 0; + goto loop; + + case 0xB8: // CLV + status &= ~st_v; + goto loop; + + case 0xD8: // CLD + status &= ~st_d; + goto loop; + + case 0xF8: // SED + status |= st_d; + goto loop; + + case 0x58: // CLI + if ( !(status & st_i) ) + goto loop; + status &= ~st_i; + handle_cli: + //dprintf( "%6d CLI\n", time() ); + this->r.status = status; // update externally-visible I flag + if ( clock_count < end_time_ ) + { + assert( clock_limit == end_time_ ); + if ( end_time_ <= irq_time_ ) + goto loop; // irq is later + if ( clock_count >= irq_time_ ) + irq_time_ = clock_count + 1; // delay IRQ until after next instruction + clock_limit = irq_time_; + goto loop; + } + // execution is stopping now, so delayed CLI must be handled by caller + result = result_cli; + goto end; + + case 0x78: // SEI + if ( status & st_i ) + goto loop; + status |= st_i; + handle_sei: + //dprintf( "%6d SEI\n", time() ); + this->r.status = status; // update externally-visible I flag + clock_limit = end_time_; + if ( clock_count < irq_time_ ) + goto loop; + result = result_sei; // IRQ will occur now, even though I flag is set + goto end; + +// Undocumented + + case 0x0C: case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: // SKW + pc++; + case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: // SKB + case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: + pc++; + case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: // NOP + goto loop; + +// Unimplemented + + case page_wrap_opcode: // HLT + if ( pc > 0x10000 ) + { + // handle wrap-around (assumes caller has put page of HLT at 0x10000) + pc = (pc - 1) & 0xffff; + clock_count -= 2; + goto loop; + } + // fall through + case 0x02: case 0x12: case 0x22: case 0x32: // HLT + case 0x42: case 0x52: case 0x62: case 0x72: + case 0x92: case 0xB2: case 0xD2: + case 0x9B: // TAS + case 0x9C: // SAY + case 0x9E: // XAS + case 0x93: // AXA + case 0x9F: // AXA + case 0x0B: // ANC + case 0x2B: // ANC + case 0xBB: // LAS + case 0x4B: // ALR + case 0x6B: // AAR + case 0x8B: // XAA + case 0xAB: // OAL + case 0xCB: // SAX + case 0x83: case 0x87: case 0x8F: case 0x97: // AXS + case 0xA3: case 0xA7: case 0xAF: case 0xB3: case 0xB7: case 0xBF: // LAX + case 0xE3: case 0xE7: case 0xEF: case 0xF3: case 0xF7: case 0xFB: case 0xFF: // INS + case 0xC3: case 0xC7: case 0xCF: case 0xD3: case 0xD7: case 0xDB: case 0xDF: // DCM + case 0x63: case 0x67: case 0x6F: case 0x73: case 0x77: case 0x7B: case 0x7F: // RRA + case 0x43: case 0x47: case 0x4F: case 0x53: case 0x57: case 0x5B: case 0x5F: // LSE + case 0x23: case 0x27: case 0x2F: case 0x33: case 0x37: case 0x3B: case 0x3F: // RLA + case 0x03: case 0x07: case 0x0F: case 0x13: case 0x17: case 0x1B: case 0x1F: // ASO + result = result_badop; + goto stop; + } + + // If this fails then the case above is missing an opcode + assert( false ); + +stop: + pc--; +end: + + { + int temp; + CALC_STATUS( temp ); + r.status = temp; + } + + base_time += clock_count; + clock_limit -= clock_count; + this->clock_count = 0; + r.pc = pc; + r.sp = GET_SP(); + r.a = a; + r.x = x; + r.y = y; + irq_time_ = LONG_MAX / 2 + 1; + + return result; +} + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Cpu.h bla2/blarg-sound/nes_apu/Nes_Cpu.h --- bla1/blarg-sound/nes_apu/Nes_Cpu.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Cpu.h 2006-08-17 20:57:43.000000000 +0300 @@ -0,0 +1,174 @@ + +// Nintendo Entertainment System (NES) 6502 CPU emulator + +// Game_Music_Emu 0.3.0 + +#ifndef NES_CPU_H +#define NES_CPU_H + +#include "blargg_common.h" + +typedef long nes_time_t; // clock cycle count +typedef unsigned nes_addr_t; // 16-bit address + +class Nes_Emu; + +class Nes_Cpu { + typedef BOOST::uint8_t uint8_t; + enum { page_bits = 11 }; + enum { page_count = 0x10000 >> page_bits }; + uint8_t const* code_map [page_count + 1]; +public: + Nes_Cpu(); + + // Memory read/write function types. Reader must return value from 0 to 255. + typedef int (*reader_t)( Nes_Emu*, nes_addr_t ); + typedef void (*writer_t)( Nes_Emu*, nes_addr_t, int data ); + void set_emu( Nes_Emu* emu ) { callback_data = emu; } + + // Clear registers, unmap memory, and map code pages to unmapped_page. + void reset( const void* unmapped_page = NULL, reader_t read = NULL, writer_t write = NULL ); + + // Memory mapping functions take a block of memory of specified 'start' address + // and 'size' in bytes. Both start address and size must be a multiple of page_size. + enum { page_size = 1L << page_bits }; + + // Map code memory (memory accessed via the program counter) + void map_code( nes_addr_t start, unsigned long size, const void* code ); + + // Set read function for address range + void set_reader( nes_addr_t start, unsigned long size, reader_t ); + + // Set write function for address range + void set_writer( nes_addr_t start, unsigned long size, writer_t ); + + // Set read and write functions for address range + void map_memory( nes_addr_t start, unsigned long size, reader_t, writer_t ); + + // Access memory as the emulated CPU does. + int read( nes_addr_t ); + void write( nes_addr_t, int data ); + uint8_t* get_code( nes_addr_t ); // non-const to allow debugger to modify code + + // Push a byte on the stack + void push_byte( int ); + + // NES 6502 registers. *Not* kept updated during a call to run(). + struct registers_t { + long pc; // more than 16 bits to allow overflow detection + BOOST::uint8_t a; + BOOST::uint8_t x; + BOOST::uint8_t y; + BOOST::uint8_t status; + BOOST::uint8_t sp; + }; + registers_t r; + + // Reasons that run() returns + enum result_t { + result_cycles, // Requested number of cycles (or more) were executed + result_sei, // I flag just set and IRQ time would generate IRQ now + result_cli, // I flag just cleared but IRQ should occur *after* next instr + result_badop // unimplemented/illegal instruction + }; + + result_t run( nes_time_t end_time_ ); + + nes_time_t time() const { return base_time + clock_count; } + void set_time( nes_time_t t ); + void end_frame( nes_time_t ); + nes_time_t end_time() const { return base_time + end_time_; } + nes_time_t irq_time() const { return base_time + irq_time_; } + void set_end_time( nes_time_t t ); + void set_irq_time( nes_time_t t ); + + // If PC exceeds 0xFFFF and encounters page_wrap_opcode, it will be silently wrapped. + enum { page_wrap_opcode = 0xF2 }; + + // One of the many opcodes that are undefined and stop CPU emulation. + enum { bad_opcode = 0xD2 }; + + // End of public interface +private: + // noncopyable + Nes_Cpu( const Nes_Cpu& ); + Nes_Cpu& operator = ( const Nes_Cpu& ); + + nes_time_t clock_limit; + nes_time_t base_time; + nes_time_t clock_count; + nes_time_t irq_time_; + nes_time_t end_time_; + + Nes_Emu* callback_data; + + enum { irq_inhibit = 0x04 }; + reader_t data_reader [page_count + 1]; // extra entry catches address overflow + writer_t data_writer [page_count + 1]; + void set_code_page( int, uint8_t const* ); + void update_clock_limit(); + +public: + // low_mem is a full page size so it can be mapped with code_map + uint8_t low_mem [page_size > 0x800 ? page_size : 0x800]; +}; + +inline BOOST::uint8_t* Nes_Cpu::get_code( nes_addr_t addr ) +{ + #if BLARGG_NONPORTABLE + return (uint8_t*) code_map [addr >> page_bits] + addr; + #else + return (uint8_t*) code_map [addr >> page_bits] + (addr & (page_size - 1)); + #endif +} + +inline void Nes_Cpu::update_clock_limit() +{ + nes_time_t t = end_time_; + if ( t > irq_time_ && !(r.status & irq_inhibit) ) + t = irq_time_; + clock_limit = t; +} + +inline void Nes_Cpu::set_end_time( nes_time_t t ) +{ + end_time_ = t - base_time; + update_clock_limit(); +} + +inline void Nes_Cpu::set_irq_time( nes_time_t t ) +{ + irq_time_ = t - base_time; + update_clock_limit(); +} + +inline void Nes_Cpu::end_frame( nes_time_t end_time_ ) +{ + base_time -= end_time_; + assert( time() >= 0 ); +} + +inline void Nes_Cpu::set_time( nes_time_t t ) +{ + t -= time(); + clock_limit -= t; + end_time_ -= t; + irq_time_ -= t; + base_time += t; +} + +inline void Nes_Cpu::push_byte( int data ) +{ + int sp = r.sp; + r.sp = (sp - 1) & 0xff; + low_mem [0x100 + sp] = data; +} + +inline void Nes_Cpu::map_memory( nes_addr_t addr, unsigned long s, reader_t r, writer_t w ) +{ + set_reader( addr, s, r ); + set_writer( addr, s, w ); +} + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Fme7_Apu.cpp bla2/blarg-sound/nes_apu/Nes_Fme7_Apu.cpp --- bla1/blarg-sound/nes_apu/Nes_Fme7_Apu.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Fme7_Apu.cpp 2006-10-15 13:40:43.000000000 +0300 @@ -0,0 +1,127 @@ + +// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ + +#include "Nes_Fme7_Apu.h" + +#include + +/* Copyright (C) 2003-2006 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 + +void Nes_Fme7_Apu::reset() +{ + last_time = 0; + + for ( int i = 0; i < osc_count; i++ ) + oscs [i].last_amp = 0; + + fme7_snapshot_t* state = this; + memset( state, 0, sizeof *state ); +} + +#include BLARGG_ENABLE_OPTIMIZER + +unsigned char Nes_Fme7_Apu::amp_table [16] = +{ + #define ENTRY( n ) (unsigned char) (n * amp_range + 0.5) + ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156), + ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624), + ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498), + ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000) + #undef ENTRY +}; + +void Nes_Fme7_Apu::run_until( blip_time_t end_time ) +{ + require( end_time >= last_time ); + + for ( int index = 0; index < osc_count; index++ ) + { + int mode = regs [7] >> index; + int vol_mode = regs [010 + index]; + int volume = amp_table [vol_mode & 0x0f]; + + if ( !oscs [index].output ) + { + // fprintf(stderr, "no output for %d\n", index); + continue; + } + //fprintf(stderr, "volume=%d\n", volume); + + // check for unsupported mode + #ifndef NDEBUG + if ( (mode & 011) <= 001 && vol_mode & 0x1f ) + dprintf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n", + mode, vol_mode & 0x1f ); + #endif + + if ( (mode & 001) | (vol_mode & 0x10) ) + volume = 0; // noise and envelope aren't supported + + // period + int const period_factor = 16; + unsigned period = (regs [index * 2 + 1] & 0x0f) * 0x100 * period_factor + + regs [index * 2] * period_factor; + if ( period < 50 ) // around 22 kHz + { + volume = 0; + if ( !period ) // on my AY-3-8910A, period doesn't have extra one added + period = period_factor; + } + + // current amplitude + int amp = volume; + if ( !phases [index] ) + amp = 0; + int delta = amp - oscs [index].last_amp; + if ( delta ) + { + oscs [index].last_amp = amp; + //fprintf(stderr, "offset! delta=%d\n", delta); + synth.offset( last_time, delta, oscs [index].output ); + } + + blip_time_t time = last_time + delays [index]; + if ( time < end_time ) + { + Blip_Buffer* const osc_output = oscs [index].output; + int delta = amp * 2 - volume; + + if ( volume ) + { + do + { + delta = -delta; + synth.offset_inline( time, delta, osc_output ); + time += period; + } + while ( time < end_time ); + + oscs [index].last_amp = (delta + volume) >> 1; + phases [index] = (delta > 0); + } + else + { + // maintain phase when silent + int count = (end_time - time + period - 1) / period; + phases [index] ^= count & 1; + time += (long) count * period; + } + } + + delays [index] = time - end_time; + } + + last_time = end_time; +} + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Fme7_Apu.h bla2/blarg-sound/nes_apu/Nes_Fme7_Apu.h --- bla1/blarg-sound/nes_apu/Nes_Fme7_Apu.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Fme7_Apu.h 2006-08-17 20:57:43.000000000 +0300 @@ -0,0 +1,135 @@ + +// Sunsoft FME-7 sound emulator + +// Game_Music_Emu 0.3.0 + +#ifndef NES_FME7_APU_H +#define NES_FME7_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct fme7_snapshot_t +{ + enum { reg_count = 14 }; + BOOST::uint8_t regs [reg_count]; + BOOST::uint8_t phases [3]; // 0 or 1 + BOOST::uint8_t latch; + BOOST::uint16_t delays [3]; // a, b, c +}; +BOOST_STATIC_ASSERT( sizeof (fme7_snapshot_t) == 24 ); + +class Nes_Fme7_Apu : private fme7_snapshot_t { +public: + Nes_Fme7_Apu(); + + // See Nes_Apu.h for reference + void reset(); + void volume( double ); + void treble_eq( blip_eq_t const& ); + void output( Blip_Buffer* ); + enum { osc_count = 3 }; + void osc_output( int index, Blip_Buffer* ); + void end_frame( blip_time_t ); + void save_snapshot( fme7_snapshot_t* ) const; + void load_snapshot( fme7_snapshot_t const& ); + + // Mask and addresses of registers + enum { addr_mask = 0xe000 }; + enum { data_addr = 0xe000 }; + enum { latch_addr = 0xc000 }; + + // (addr & addr_mask) == latch_addr + void write_latch( int ); + + // (addr & addr_mask) == data_addr + void write_data( blip_time_t, int data ); + + // End of public interface +private: + // noncopyable + Nes_Fme7_Apu( const Nes_Fme7_Apu& ); + Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& ); + + static unsigned char amp_table [16]; + + struct { + Blip_Buffer* output; + int last_amp; + } oscs [osc_count]; + blip_time_t last_time; + + enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff + Blip_Synth synth; + + void run_until( blip_time_t ); +}; + +inline void Nes_Fme7_Apu::volume( double v ) +{ + synth.volume( 0.38 / amp_range * v ); // to do: fine-tune +} + +inline void Nes_Fme7_Apu::treble_eq( blip_eq_t const& eq ) +{ + synth.treble_eq( eq ); +} + +inline void Nes_Fme7_Apu::osc_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Nes_Fme7_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buf ); +} + +inline Nes_Fme7_Apu::Nes_Fme7_Apu() +{ + output( NULL ); + volume( 1.0 ); + reset(); +} + +inline void Nes_Fme7_Apu::write_latch( int data ) { latch = data; } + +inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data ) +{ + if ( (unsigned) latch >= reg_count ) + { + #ifdef dprintf + dprintf( "FME7 write to %02X (past end of sound registers)\n", (int) latch ); + #endif + return; + } + + run_until( time ); + regs [latch] = data; +} + +inline void Nes_Fme7_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +inline void Nes_Fme7_Apu::save_snapshot( fme7_snapshot_t* out ) const +{ + *out = *this; +} + +inline void Nes_Fme7_Apu::load_snapshot( fme7_snapshot_t const& in ) +{ + reset(); + fme7_snapshot_t* state = this; + *state = in; +} + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Namco_Apu.cpp bla2/blarg-sound/nes_apu/Nes_Namco_Apu.cpp --- bla1/blarg-sound/nes_apu/Nes_Namco_Apu.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Namco_Apu.cpp 2006-08-17 20:57:43.000000000 +0300 @@ -0,0 +1,151 @@ + +// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ + +#include "Nes_Namco_Apu.h" + +/* Copyright (C) 2003-2006 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 + +Nes_Namco_Apu::Nes_Namco_Apu() +{ + output( NULL ); + volume( 1.0 ); + reset(); +} + +Nes_Namco_Apu::~Nes_Namco_Apu() +{ +} + +void Nes_Namco_Apu::reset() +{ + last_time = 0; + addr_reg = 0; + + int i; + for ( i = 0; i < reg_count; i++ ) + reg [i] = 0; + + for ( i = 0; i < osc_count; i++ ) + { + Namco_Osc& osc = oscs [i]; + osc.delay = 0; + osc.last_amp = 0; + osc.wave_pos = 0; + } +} + +void Nes_Namco_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buf ); +} + +/* +void Nes_Namco_Apu::reflect_state( Tagged_Data& data ) +{ + reflect_int16( data, 'ADDR', &addr_reg ); + + static const char hex [17] = "0123456789ABCDEF"; + int i; + for ( i = 0; i < reg_count; i++ ) + reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] ); + + for ( i = 0; i < osc_count; i++ ) + { + reflect_int32( data, 'DLY0' + i, &oscs [i].delay ); + reflect_int16( data, 'POS0' + i, &oscs [i].wave_pos ); + } +} +*/ + +void Nes_Namco_Apu::end_frame( nes_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +#include BLARGG_ENABLE_OPTIMIZER + +void Nes_Namco_Apu::run_until( nes_time_t nes_end_time ) +{ + int active_oscs = (reg [0x7F] >> 4 & 7) + 1; + for ( int i = osc_count - active_oscs; i < osc_count; i++ ) + { + Namco_Osc& osc = oscs [i]; + Blip_Buffer* output = osc.output; + if ( !output ) + continue; + + blip_resampled_time_t time = + output->resampled_time( last_time ) + osc.delay; + blip_resampled_time_t end_time = output->resampled_time( nes_end_time ); + osc.delay = 0; + if ( time < end_time ) + { + const BOOST::uint8_t* osc_reg = ® [i * 8 + 0x40]; + if ( !(osc_reg [4] & 0xE0) ) + continue; + + int volume = osc_reg [7] & 15; + if ( !volume ) + continue; + + long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0]; + if ( freq < 64 * active_oscs ) + continue; // prevent low frequencies from excessively delaying freq changes + blip_resampled_time_t period = + output->resampled_duration( 983040 ) / freq * active_oscs; + + int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4; + if ( !wave_size ) + continue; + + int last_amp = osc.last_amp; + int wave_pos = osc.wave_pos; + + do + { + // read wave sample + int addr = wave_pos + osc_reg [6]; + int sample = reg [addr >> 1] >> (addr << 2 & 4); + wave_pos++; + sample = (sample & 15) * volume; + + // output impulse if amplitude changed + int delta = sample - last_amp; + if ( delta ) + { + last_amp = sample; + synth.offset_resampled( time, delta, output ); + } + + // next sample + time += period; + if ( wave_pos >= wave_size ) + wave_pos = 0; + } + while ( time < end_time ); + + osc.wave_pos = wave_pos; + osc.last_amp = last_amp; + } + osc.delay = time - end_time; + } + + last_time = nes_end_time; +} + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Namco_Apu.h bla2/blarg-sound/nes_apu/Nes_Namco_Apu.h --- bla1/blarg-sound/nes_apu/Nes_Namco_Apu.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Namco_Apu.h 2006-08-17 20:57:44.000000000 +0300 @@ -0,0 +1,104 @@ + +// Namco 106 sound chip emulator + +// Nes_Snd_Emu 0.1.7 + +#ifndef NES_NAMCO_APU_H +#define NES_NAMCO_APU_H + +#include "Nes_Apu.h" + +struct namco_snapshot_t; + +class Nes_Namco_Apu { +public: + Nes_Namco_Apu(); + ~Nes_Namco_Apu(); + + // See Nes_Apu.h for reference. + void volume( double ); + void treble_eq( const blip_eq_t& ); + void output( Blip_Buffer* ); + enum { osc_count = 8 }; + void osc_output( int index, Blip_Buffer* ); + void reset(); + void end_frame( nes_time_t ); + + // Read/write data register is at 0x4800 + enum { data_reg_addr = 0x4800 }; + void write_data( nes_time_t, int ); + int read_data(); + + // Write-only address register is at 0xF800 + enum { addr_reg_addr = 0xF800 }; + void write_addr( int ); + + // to do: implement save/restore + void save_snapshot( namco_snapshot_t* out ) const; + void load_snapshot( namco_snapshot_t const& ); + +private: + // noncopyable + Nes_Namco_Apu( const Nes_Namco_Apu& ); + Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& ); + + struct Namco_Osc { + long delay; + Blip_Buffer* output; + short last_amp; + short wave_pos; + }; + + Namco_Osc oscs [osc_count]; + + nes_time_t last_time; + int addr_reg; + + enum { reg_count = 0x80 }; + BOOST::uint8_t reg [reg_count]; + Blip_Synth synth; + + BOOST::uint8_t& access(); + void run_until( nes_time_t ); +}; +/* +struct namco_snapshot_t +{ + BOOST::uint8_t regs [0x80]; + BOOST::uint8_t addr; + BOOST::uint8_t unused; + BOOST::uint8_t positions [8]; + BOOST::uint32_t delays [8]; +}; +*/ + +inline BOOST::uint8_t& Nes_Namco_Apu::access() +{ + int addr = addr_reg & 0x7f; + if ( addr_reg & 0x80 ) + addr_reg = (addr + 1) | 0x80; + return reg [addr]; +} + +inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / osc_count * v ); } + +inline void Nes_Namco_Apu::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); } + +inline void Nes_Namco_Apu::write_addr( int v ) { addr_reg = v; } + +inline int Nes_Namco_Apu::read_data() { return access(); } + +inline void Nes_Namco_Apu::osc_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Nes_Namco_Apu::write_data( nes_time_t time, int data ) +{ + run_until( time ); + access() = data; +} + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Oscs.cpp bla2/blarg-sound/nes_apu/Nes_Oscs.cpp --- bla1/blarg-sound/nes_apu/Nes_Oscs.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Oscs.cpp 2006-08-17 20:57:44.000000000 +0300 @@ -0,0 +1,498 @@ + +// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ + +#include "Nes_Apu.h" + +/* Copyright (C) 2003-2006 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 + +// Nes_Osc + +void Nes_Osc::clock_length( int halt_mask ) +{ + if ( length_counter && !(regs [0] & halt_mask) ) + length_counter--; +} + +void Nes_Envelope::clock_envelope() +{ + int period = regs [0] & 15; + if ( reg_written [3] ) { + reg_written [3] = false; + env_delay = period; + envelope = 15; + } + else if ( --env_delay < 0 ) { + env_delay = period; + if ( envelope | (regs [0] & 0x20) ) + envelope = (envelope - 1) & 15; + } +} + +int Nes_Envelope::volume() const +{ + return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope; +} + +// Nes_Square + +void Nes_Square::clock_sweep( int negative_adjust ) +{ + int sweep = regs [1]; + + if ( --sweep_delay < 0 ) + { + reg_written [1] = true; + + int period = this->period(); + int shift = sweep & shift_mask; + if ( shift && (sweep & 0x80) && period >= 8 ) + { + int offset = period >> shift; + + if ( sweep & negate_flag ) + offset = negative_adjust - offset; + + if ( period + offset < 0x800 ) + { + period += offset; + // rewrite period + regs [2] = period & 0xff; + regs [3] = (regs [3] & ~7) | ((period >> 8) & 7); + } + } + } + + if ( reg_written [1] ) { + reg_written [1] = false; + sweep_delay = (sweep >> 4) & 7; + } +} + +void Nes_Square::run( nes_time_t time, nes_time_t end_time ) +{ + if ( !output ) + return; + + const int volume = this->volume(); + const int period = this->period(); + int offset = period >> (regs [1] & shift_mask); + if ( regs [1] & negate_flag ) + offset = 0; + + const int timer_period = (period + 1) * 2; + if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) + { + if ( last_amp ) { + synth.offset( time, -last_amp, output ); + last_amp = 0; + } + + time += delay; + if ( time < end_time ) + { + // maintain proper phase + int count = (end_time - time + timer_period - 1) / timer_period; + phase = (phase + count) & (phase_range - 1); + time += (long) count * timer_period; + } + } + else + { + // handle duty select + int duty_select = (regs [0] >> 6) & 3; + int duty = 1 << duty_select; // 1, 2, 4, 2 + int amp = 0; + if ( duty_select == 3 ) { + duty = 2; // negated 25% + amp = volume; + } + if ( phase < duty ) + amp ^= volume; + + int delta = update_amp( amp ); + if ( delta ) + synth.offset( time, delta, output ); + + time += delay; + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + const Synth& synth = this->synth; + int delta = amp * 2 - volume; + int phase = this->phase; + + do { + phase = (phase + 1) & (phase_range - 1); + if ( phase == 0 || phase == duty ) { + delta = -delta; + synth.offset_inline( time, delta, output ); + } + time += timer_period; + } + while ( time < end_time ); + + last_amp = (delta + volume) >> 1; + this->phase = phase; + } + } + + delay = time - end_time; +} + +// Nes_Triangle + +void Nes_Triangle::clock_linear_counter() +{ + if ( reg_written [3] ) + linear_counter = regs [0] & 0x7f; + else if ( linear_counter ) + linear_counter--; + + if ( !(regs [0] & 0x80) ) + reg_written [3] = false; +} + +inline int Nes_Triangle::calc_amp() const +{ + int amp = phase_range - phase; + if ( amp < 0 ) + amp = phase - (phase_range + 1); + return amp; +} + +void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) +{ + if ( !output ) + return; + + // to do: track phase when period < 3 + // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. + + int delta = update_amp( calc_amp() ); + if ( delta ) + synth.offset( time, delta, output ); + + time += delay; + const int timer_period = period() + 1; + if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) + { + time = end_time; + } + else if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + + int phase = this->phase; + int volume = 1; + if ( phase > phase_range ) { + phase -= phase_range; + volume = -volume; + } + + do { + if ( --phase == 0 ) { + phase = phase_range; + volume = -volume; + } + else { + synth.offset_inline( time, volume, output ); + } + + time += timer_period; + } + while ( time < end_time ); + + if ( volume < 0 ) + phase += phase_range; + this->phase = phase; + last_amp = calc_amp(); + } + delay = time - end_time; +} + +// Nes_Dmc + +void Nes_Dmc::reset() +{ + address = 0; + dac = 0; + buf = 0; + bits_remain = 1; + bits = 0; + buf_full = false; + silence = true; + next_irq = Nes_Apu::no_irq; + irq_flag = false; + irq_enabled = false; + + Nes_Osc::reset(); + period = 0x1ac; +} + +void Nes_Dmc::recalc_irq() +{ + nes_time_t irq = Nes_Apu::no_irq; + if ( irq_enabled && length_counter ) + irq = apu->last_dmc_time + delay + + ((length_counter - 1) * 8 + bits_remain - 1) * nes_time_t (period) + 1; + if ( irq != next_irq ) { + next_irq = irq; + apu->irq_changed(); + } +} + +int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const +{ + if ( last_read ) + *last_read = time; + + if ( length_counter == 0 ) + return 0; // not reading + + long first_read = next_read_time(); + long avail = time - first_read; + if ( avail <= 0 ) + return 0; + + int count = (avail - 1) / (period * 8) + 1; + if ( !(regs [0] & loop_flag) && count > length_counter ) + count = length_counter; + + if ( last_read ) { + *last_read = first_read + (count - 1) * (period * 8) + 1; + assert( *last_read <= time ); + assert( count == count_reads( *last_read, NULL ) ); + assert( count - 1 == count_reads( *last_read - 1, NULL ) ); + } + + return count; +} + +static const short dmc_period_table [2] [16] = { + {0x1ac, 0x17c, 0x154, 0x140, 0x11e, 0x0fe, 0x0e2, 0x0d6, // NTSC + 0x0be, 0x0a0, 0x08e, 0x080, 0x06a, 0x054, 0x048, 0x036}, + + {0x18e, 0x161, 0x13c, 0x129, 0x10a, 0x0ec, 0x0d2, 0x0c7, // PAL (totally untested) + 0x0b1, 0x095, 0x084, 0x077, 0x062, 0x04e, 0x043, 0x032} // to do: verify PAL periods +}; + +inline void Nes_Dmc::reload_sample() +{ + address = 0x4000 + regs [2] * 0x40; + length_counter = regs [3] * 0x10 + 1; +} + +static const unsigned char dac_table [128] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14, + 15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27, + 27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38, + 39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49, + 50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59, + 59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67, + 68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75, + 76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83, +}; + +void Nes_Dmc::write_register( int addr, int data ) +{ + if ( addr == 0 ) + { + period = dmc_period_table [pal_mode] [data & 15]; + irq_enabled = (data & 0xc0) == 0x80; // enabled only if loop disabled + irq_flag &= irq_enabled; + recalc_irq(); + } + else if ( addr == 1 ) + { + int old_dac = dac; + dac = data & 0x7F; + + // adjust last_amp so that "pop" amplitude will be properly non-linear + // with respect to change in dac + int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]); + if ( !nonlinear ) + last_amp = faked_nonlinear; + } +} + +void Nes_Dmc::start() +{ + reload_sample(); + fill_buffer(); + recalc_irq(); +} + +void Nes_Dmc::fill_buffer() +{ + if ( !buf_full && length_counter ) + { + require( rom_reader ); // rom_reader must be set + buf = rom_reader( rom_reader_data, 0x8000u + address ); + address = (address + 1) & 0x7FFF; + buf_full = true; + if ( --length_counter == 0 ) + { + if ( regs [0] & loop_flag ) { + reload_sample(); + } + else { + apu->osc_enables &= ~0x10; + irq_flag = irq_enabled; + next_irq = Nes_Apu::no_irq; + apu->irq_changed(); + } + } + } +} + +void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) +{ + int delta = update_amp( dac ); + if ( !output ) + silence = true; + else if ( delta ) + synth.offset( time, delta, output ); + + time += delay; + if ( time < end_time ) + { + int bits_remain = this->bits_remain; + if ( silence && !buf_full ) + { + int count = (end_time - time + period - 1) / period; + bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1; + time += count * period; + } + else + { + Blip_Buffer* const output = this->output; + const int period = this->period; + int bits = this->bits; + int dac = this->dac; + + do + { + if ( !silence ) + { + int step = (bits & 1) * 4 - 2; + bits >>= 1; + if ( unsigned (dac + step) <= 0x7F ) { + dac += step; + synth.offset_inline( time, step, output ); + } + } + + time += period; + + if ( --bits_remain == 0 ) + { + bits_remain = 8; + if ( !buf_full ) { + silence = true; + } + else { + silence = false; + bits = buf; + buf_full = false; + if ( !output ) + silence = true; + fill_buffer(); + } + } + } + while ( time < end_time ); + + this->dac = dac; + this->last_amp = dac; + this->bits = bits; + } + this->bits_remain = bits_remain; + } + delay = time - end_time; +} + +// Nes_Noise + +#include BLARGG_ENABLE_OPTIMIZER + +static const short noise_period_table [16] = { + 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0, + 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4 +}; + +void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) +{ + if ( !output ) + return; + + const int volume = this->volume(); + int amp = (noise & 1) ? volume : 0; + int delta = update_amp( amp ); + if ( delta ) + synth.offset( time, delta, output ); + + time += delay; + if ( time < end_time ) + { + const int mode_flag = 0x80; + + int period = noise_period_table [regs [2] & 15]; + if ( !volume ) + { + // round to next multiple of period + time += (end_time - time + period - 1) / period * period; + + // approximate noise cycling while muted, by shuffling up noise register + // to do: precise muted noise cycling? + if ( !(regs [2] & mode_flag) ) { + int feedback = (noise << 13) ^ (noise << 14); + noise = (feedback & 0x4000) | (noise >> 1); + } + } + else + { + Blip_Buffer* const output = this->output; + + // using resampled time avoids conversion in synth.offset() + blip_resampled_time_t rperiod = output->resampled_duration( period ); + blip_resampled_time_t rtime = output->resampled_time( time ); + + int noise = this->noise; + int delta = amp * 2 - volume; + const int tap = (regs [2] & mode_flag ? 8 : 13); + + do { + int feedback = (noise << tap) ^ (noise << 14); + time += period; + + if ( (noise + 1) & 2 ) { + // bits 0 and 1 of noise differ + delta = -delta; + synth.offset_resampled( rtime, delta, output ); + } + + rtime += rperiod; + noise = (feedback & 0x4000) | (noise >> 1); + } + while ( time < end_time ); + + last_amp = (delta + volume) >> 1; + this->noise = noise; + } + } + + delay = time - end_time; +} + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Oscs.h bla2/blarg-sound/nes_apu/Nes_Oscs.h --- bla1/blarg-sound/nes_apu/Nes_Oscs.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Oscs.h 2006-08-17 20:57:44.000000000 +0300 @@ -0,0 +1,146 @@ + +// Private oscillators used by Nes_Apu + +// Nes_Snd_Emu 0.1.7 + +#ifndef NES_OSCS_H +#define NES_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Nes_Apu; + +struct Nes_Osc +{ + unsigned char regs [4]; + bool reg_written [4]; + Blip_Buffer* output; + int length_counter;// length counter (0 if unused by oscillator) + int delay; // delay until next (potential) transition + int last_amp; // last amplitude oscillator was outputting + + void clock_length( int halt_mask ); + int period() const { + return (regs [3] & 7) * 0x100 + (regs [2] & 0xff); + } + void reset() { + delay = 0; + last_amp = 0; + } + int update_amp( int amp ) { + int delta = amp - last_amp; + last_amp = amp; + return delta; + } +}; + +struct Nes_Envelope : Nes_Osc +{ + int envelope; + int env_delay; + + void clock_envelope(); + int volume() const; + void reset() { + envelope = 0; + env_delay = 0; + Nes_Osc::reset(); + } +}; + +// Nes_Square +struct Nes_Square : Nes_Envelope +{ + enum { negate_flag = 0x08 }; + enum { shift_mask = 0x07 }; + enum { phase_range = 8 }; + int phase; + int sweep_delay; + + typedef Blip_Synth Synth; + Synth const& synth; // shared between squares + + Nes_Square( Synth const* s ) : synth( *s ) { } + + void clock_sweep( int adjust ); + void run( nes_time_t, nes_time_t ); + void reset() { + sweep_delay = 0; + Nes_Envelope::reset(); + } +}; + +// Nes_Triangle +struct Nes_Triangle : Nes_Osc +{ + enum { phase_range = 16 }; + int phase; + int linear_counter; + Blip_Synth synth; + + int calc_amp() const; + void run( nes_time_t, nes_time_t ); + void clock_linear_counter(); + void reset() { + linear_counter = 0; + phase = phase_range; + Nes_Osc::reset(); + } +}; + +// Nes_Noise +struct Nes_Noise : Nes_Envelope +{ + int noise; + Blip_Synth synth; + + void run( nes_time_t, nes_time_t ); + void reset() { + noise = 1 << 14; + Nes_Envelope::reset(); + } +}; + +// Nes_Dmc +struct Nes_Dmc : Nes_Osc +{ + int address; // address of next byte to read + int period; + //int length_counter; // bytes remaining to play (already defined in Nes_Osc) + int buf; + int bits_remain; + int bits; + bool buf_full; + bool silence; + + enum { loop_flag = 0x40 }; + + int dac; + + nes_time_t next_irq; + bool irq_enabled; + bool irq_flag; + bool pal_mode; + bool nonlinear; + + int (*rom_reader)( void*, nes_addr_t ); // needs to be initialized to rom read function + void* rom_reader_data; + + Nes_Apu* apu; + + Blip_Synth synth; + + void start(); + void write_register( int, int ); + void run( nes_time_t, nes_time_t ); + void recalc_irq(); + void fill_buffer(); + void reload_sample(); + void reset(); + int count_reads( nes_time_t, nes_time_t* ) const; + nes_time_t next_read_time() const; +}; + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Vrc6_Apu.cpp bla2/blarg-sound/nes_apu/Nes_Vrc6_Apu.cpp --- bla1/blarg-sound/nes_apu/Nes_Vrc6_Apu.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Vrc6_Apu.cpp 2006-08-17 20:57:44.000000000 +0300 @@ -0,0 +1,219 @@ + +// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ + +#include "Nes_Vrc6_Apu.h" + +/* Copyright (C) 2003-2006 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 + +Nes_Vrc6_Apu::Nes_Vrc6_Apu() +{ + output( NULL ); + volume( 1.0 ); + reset(); +} + +Nes_Vrc6_Apu::~Nes_Vrc6_Apu() +{ +} + +void Nes_Vrc6_Apu::reset() +{ + last_time = 0; + for ( int i = 0; i < osc_count; i++ ) + { + Vrc6_Osc& osc = oscs [i]; + for ( int j = 0; j < reg_count; j++ ) + osc.regs [j] = 0; + osc.delay = 0; + osc.last_amp = 0; + osc.phase = 1; + osc.amp = 0; + } +} + +void Nes_Vrc6_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buf ); +} + +void Nes_Vrc6_Apu::run_until( nes_time_t time ) +{ + require( time >= last_time ); + run_square( oscs [0], time ); + run_square( oscs [1], time ); + run_saw( time ); + last_time = time; +} + +void Nes_Vrc6_Apu::write_osc( nes_time_t time, int osc_index, int reg, int data ) +{ + require( (unsigned) osc_index < osc_count ); + require( (unsigned) reg < reg_count ); + + run_until( time ); + oscs [osc_index].regs [reg] = data; +} + +void Nes_Vrc6_Apu::end_frame( nes_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +void Nes_Vrc6_Apu::save_snapshot( vrc6_snapshot_t* out ) const +{ + out->saw_amp = oscs [2].amp; + for ( int i = 0; i < osc_count; i++ ) + { + Vrc6_Osc const& osc = oscs [i]; + for ( int r = 0; r < reg_count; r++ ) + out->regs [i] [r] = osc.regs [r]; + + out->delays [i] = osc.delay; + out->phases [i] = osc.phase; + } +} + +void Nes_Vrc6_Apu::load_snapshot( vrc6_snapshot_t const& in ) +{ + reset(); + oscs [2].amp = in.saw_amp; + for ( int i = 0; i < osc_count; i++ ) + { + Vrc6_Osc& osc = oscs [i]; + for ( int r = 0; r < reg_count; r++ ) + osc.regs [r] = in.regs [i] [r]; + + osc.delay = in.delays [i]; + osc.phase = in.phases [i]; + } + if ( !oscs [2].phase ) + oscs [2].phase = 1; +} + +#include BLARGG_ENABLE_OPTIMIZER + +void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, nes_time_t end_time ) +{ + Blip_Buffer* output = osc.output; + if ( !output ) + return; + + int volume = osc.regs [0] & 15; + if ( !(osc.regs [2] & 0x80) ) + volume = 0; + + int gate = osc.regs [0] & 0x80; + int duty = ((osc.regs [0] >> 4) & 7) + 1; + int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp; + nes_time_t time = last_time; + if ( delta ) + { + osc.last_amp += delta; + square_synth.offset( time, delta, output ); + } + + time += osc.delay; + osc.delay = 0; + int period = osc.period(); + if ( volume && !gate && period > 4 ) + { + if ( time < end_time ) + { + int phase = osc.phase; + + do + { + phase++; + if ( phase == 16 ) + { + phase = 0; + osc.last_amp = volume; + square_synth.offset( time, volume, output ); + } + if ( phase == duty ) + { + osc.last_amp = 0; + square_synth.offset( time, -volume, output ); + } + time += period; + } + while ( time < end_time ); + + osc.phase = phase; + } + osc.delay = time - end_time; + } +} + +void Nes_Vrc6_Apu::run_saw( nes_time_t end_time ) +{ + Vrc6_Osc& osc = oscs [2]; + Blip_Buffer* output = osc.output; + if ( !output ) + return; + + int amp = osc.amp; + int amp_step = osc.regs [0] & 0x3F; + nes_time_t time = last_time; + int last_amp = osc.last_amp; + if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) ) + { + osc.delay = 0; + int delta = (amp >> 3) - last_amp; + last_amp = amp >> 3; + saw_synth.offset( time, delta, output ); + } + else + { + time += osc.delay; + if ( time < end_time ) + { + int period = osc.period() * 2; + int phase = osc.phase; + + do + { + if ( --phase == 0 ) + { + phase = 7; + amp = 0; + } + + int delta = (amp >> 3) - last_amp; + if ( delta ) + { + last_amp = amp >> 3; + saw_synth.offset( time, delta, output ); + } + + time += period; + amp = (amp + amp_step) & 0xFF; + } + while ( time < end_time ); + + osc.phase = phase; + osc.amp = amp; + } + + osc.delay = time - end_time; + } + + osc.last_amp = last_amp; +} + diff -bNaHudr bla1/blarg-sound/nes_apu/Nes_Vrc6_Apu.h bla2/blarg-sound/nes_apu/Nes_Vrc6_Apu.h --- bla1/blarg-sound/nes_apu/Nes_Vrc6_Apu.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nes_Vrc6_Apu.h 2006-08-17 20:57:44.000000000 +0300 @@ -0,0 +1,99 @@ + +// Konami VRC6 sound chip emulator + +// Nes_Snd_Emu 0.1.7 + +#ifndef NES_VRC6_APU_H +#define NES_VRC6_APU_H + +#include "Nes_Apu.h" +#include "Blip_Buffer.h" + +struct vrc6_snapshot_t; + +class Nes_Vrc6_Apu { +public: + Nes_Vrc6_Apu(); + ~Nes_Vrc6_Apu(); + + // See Nes_Apu.h for reference + void reset(); + void volume( double ); + void treble_eq( blip_eq_t const& ); + void output( Blip_Buffer* ); + enum { osc_count = 3 }; + void osc_output( int index, Blip_Buffer* ); + void end_frame( nes_time_t ); + void save_snapshot( vrc6_snapshot_t* ) const; + void load_snapshot( vrc6_snapshot_t const& ); + + // Oscillator 0 write-only registers are at $9000-$9002 + // Oscillator 1 write-only registers are at $A000-$A002 + // Oscillator 2 write-only registers are at $B000-$B002 + enum { reg_count = 3 }; + enum { base_addr = 0x9000 }; + enum { addr_step = 0x1000 }; + void write_osc( nes_time_t, int osc, int reg, int data ); + +private: + // noncopyable + Nes_Vrc6_Apu( const Nes_Vrc6_Apu& ); + Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& ); + + struct Vrc6_Osc + { + BOOST::uint8_t regs [3]; + Blip_Buffer* output; + int delay; + int last_amp; + int phase; + int amp; // only used by saw + + int period() const + { + return (regs [2] & 0x0f) * 0x100L + regs [1] + 1; + } + }; + + Vrc6_Osc oscs [osc_count]; + nes_time_t last_time; + + Blip_Synth saw_synth; + Blip_Synth square_synth; + + void run_until( nes_time_t ); + void run_square( Vrc6_Osc& osc, nes_time_t ); + void run_saw( nes_time_t ); +}; + +struct vrc6_snapshot_t +{ + BOOST::uint8_t regs [3] [3]; + BOOST::uint8_t saw_amp; + BOOST::uint16_t delays [3]; + BOOST::uint8_t phases [3]; + BOOST::uint8_t unused; +}; +BOOST_STATIC_ASSERT( sizeof (vrc6_snapshot_t) == 20 ); + +inline void Nes_Vrc6_Apu::osc_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Nes_Vrc6_Apu::volume( double v ) +{ + double const factor = 0.0967 * 2; + saw_synth.volume( factor / 31 * v ); + square_synth.volume( factor * 0.5 / 15 * v ); +} + +inline void Nes_Vrc6_Apu::treble_eq( blip_eq_t const& eq ) +{ + saw_synth.treble_eq( eq ); + square_synth.treble_eq( eq ); +} + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/Nonlinear_Buffer.cpp bla2/blarg-sound/nes_apu/Nonlinear_Buffer.cpp --- bla1/blarg-sound/nes_apu/Nonlinear_Buffer.cpp 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nonlinear_Buffer.cpp 2005-10-03 13:45:02.000000000 +0300 @@ -0,0 +1,189 @@ + +// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ + +#include "Nonlinear_Buffer.h" + +#include "Nes_Apu.h" + +/* Library Copyright (C) 2003-2005 Shay Green. 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 +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 library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include BLARGG_SOURCE_BEGIN + +// Nonlinear_Buffer + +Nonlinear_Buffer::Nonlinear_Buffer() : + Multi_Buffer( 1 ) +{ +} + +Nonlinear_Buffer::~Nonlinear_Buffer() +{ +} + +void Nonlinear_Buffer::enable_nonlinearity( Nes_Apu& apu, bool b ) +{ + if ( b ) + clear(); + nonlinearizer.enable( apu, b ); + for ( int i = 0; i < apu.osc_count; i++ ) + apu.osc_output( i, (i >= 2 ? &tnd : &buf) ); +} + +blargg_err_t Nonlinear_Buffer::sample_rate( long rate, int msec ) +{ + BLARGG_RETURN_ERR( buf.sample_rate( rate, msec ) ); + BLARGG_RETURN_ERR( tnd.sample_rate( rate, msec ) ); + return Multi_Buffer::sample_rate( buf.sample_rate(), buf.length() ); +} + +void Nonlinear_Buffer::clock_rate( long rate ) +{ + buf.clock_rate( rate ); + tnd.clock_rate( rate ); +} + +void Nonlinear_Buffer::bass_freq( int freq ) +{ + buf.bass_freq( freq ); + tnd.bass_freq( freq ); +} + +void Nonlinear_Buffer::clear() +{ + nonlinearizer.clear(); + buf.clear(); + tnd.clear(); +} + +Nonlinear_Buffer::channel_t Nonlinear_Buffer::channel( int i ) +{ + channel_t c; + c.center = &buf; + if ( 2 <= i && i <= 4 ) + c.center = &tnd; // only use for triangle, noise, and dmc + c.left = c.center; + c.right = c.center; + return c; +} + +void Nonlinear_Buffer::end_frame( blip_time_t length, bool ) +{ + buf.end_frame( length ); + tnd.end_frame( length ); +} + +long Nonlinear_Buffer::samples_avail() const +{ + return buf.samples_avail(); +} + +#include BLARGG_ENABLE_OPTIMIZER + +long Nonlinear_Buffer::read_samples( blip_sample_t* out, long count ) +{ + count = nonlinearizer.make_nonlinear( tnd, count ); + if ( count ) + { + Blip_Reader lin; + Blip_Reader nonlin; + + int lin_bass = lin.begin( buf ); + int nonlin_bass = nonlin.begin( tnd ); + + for ( int n = count; n--; ) + { + int s = lin.read() + nonlin.read(); + lin.next( lin_bass ); + nonlin.next( nonlin_bass ); + *out++ = s; + + if ( (BOOST::int16_t) s != s ) + out [-1] = 0x7FFF - (s >> 24); + } + + lin.end( buf ); + nonlin.end( tnd ); + + buf.remove_samples( count ); + tnd.remove_samples( count ); + } + + return count; +} + +// Nes_Nonlinearizer + +Nes_Nonlinearizer::Nes_Nonlinearizer() +{ + nonlinear = false; + + double gain = 0x7fff * 1.3; + // don't use entire range, so any overflow will stay within table + int const range = half * 0.75; // to do: must match that in Nes_Apu.cpp + for ( int i = 0; i < half * 2; i++ ) + { + int out = i << shift; + if ( i > half ) + { + int j = i - half; + if ( j >= range ) + j = range - 1; + double n = 202.0 / (range - 1) * j; + double d = 163.67 / (24329.0 / n + 100); + out = int (d * gain) + 0x8000; + assert( out < 0x10000 ); + } + table [i] = out; + } + clear(); +} + +void Nes_Nonlinearizer::enable( Nes_Apu& apu, bool b ) +{ + nonlinear = b; + if ( b ) + apu.enable_nonlinear( 1.0 ); + else + apu.volume( 1.0 ); +} + +long Nes_Nonlinearizer::make_nonlinear( Blip_Buffer& buf, long count ) +{ + long avail = buf.samples_avail(); + if ( count > avail ) + count = avail; + + if ( count && nonlinear ) + { + const int zero_offset = 0x7f7f; // to do: use private constant from Blip_Buffer.h + + #define ENTRY( s ) (table [((s) >> shift) & entry_mask]) + + BOOST::uint16_t* p = buf.buffer_; + unsigned prev = ENTRY( accum ); + long accum = this->accum; + + for ( unsigned n = count; n--; ) + { + accum += (long) *p - zero_offset; + check( (accum >> shift) < half * 2 ); + unsigned entry = ENTRY( accum ); + *p++ = entry - prev + zero_offset; + prev = entry; + } + + this->accum = accum; + } + + return count; +} + diff -bNaHudr bla1/blarg-sound/nes_apu/Nonlinear_Buffer.h bla2/blarg-sound/nes_apu/Nonlinear_Buffer.h --- bla1/blarg-sound/nes_apu/Nonlinear_Buffer.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/Nonlinear_Buffer.h 2005-10-03 13:44:34.000000000 +0300 @@ -0,0 +1,65 @@ + +// NES non-linear audio output handling. + +// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. + +#ifndef NONLINEAR_BUFFER_H +#define NONLINEAR_BUFFER_H + +#include "Multi_Buffer.h" +class Nes_Apu; + +// Use to make samples non-linear in Blip_Buffer used for triangle, noise, and DMC only +class Nes_Nonlinearizer { +public: + Nes_Nonlinearizer(); + + // Must be called when buffer is cleared + void clear() { accum = 0x8000; } + + // Enable/disable non-linear output + void enable( Nes_Apu&, bool = true ); + + // Make at most 'count' samples in buffer non-linear and return number + // of samples modified. This many samples must then be read out of the buffer. + long make_nonlinear( Blip_Buffer&, long count ); + +private: + enum { shift = 5 }; + enum { half = 0x8000 >> shift }; + enum { entry_mask = half * 2 - 1 }; + BOOST::uint16_t table [half * 2]; + long accum; + bool nonlinear; +}; + +class Nonlinear_Buffer : public Multi_Buffer { +public: + Nonlinear_Buffer(); + ~Nonlinear_Buffer(); + + // Enable/disable non-linear output + void enable_nonlinearity( Nes_Apu&, bool = true ); + + // Blip_Buffer to output other sound chips to + Blip_Buffer* buffer() { return &buf; } + + // See Multi_Buffer.h + blargg_err_t sample_rate( long rate, int msec = blip_default_length ); + Multi_Buffer::sample_rate; + 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 ); + +private: + Blip_Buffer buf; + Blip_Buffer tnd; + Nes_Nonlinearizer nonlinearizer; +}; + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/blargg_common.h bla2/blarg-sound/nes_apu/blargg_common.h --- bla1/blarg-sound/nes_apu/blargg_common.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/blargg_common.h 2006-10-15 13:40:43.000000000 +0300 @@ -0,0 +1,242 @@ + +// Sets up common environment for Shay Green's libraries. +// +// To change configuration options, modify blargg_config.h, not this file. + +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +// HAVE_CONFIG_H: If defined, include user's "config.h" first (which *can* +// re-include blargg_common.h if it needs to) +#ifdef HAVE_CONFIG_H + #undef BLARGG_COMMON_H + #include "config.h" + #define BLARGG_COMMON_H +#endif + +// BLARGG_NONPORTABLE: If defined to 1, platform-specific (and possibly non-portable) +// optimizations are used. Defaults to off. Report any problems that occur only when +// this is enabled. +#ifndef BLARGG_NONPORTABLE + #define BLARGG_NONPORTABLE 0 +#endif + +// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only +// one must be #defined to 1. Only needed if something actually depends on byte order. +#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) + #if defined (MSB_FIRST) || defined (__powerc) || defined (macintosh) || \ + defined (WORDS_BIGENDIAN) || defined (__BIG_ENDIAN__) + #define BLARGG_BIG_ENDIAN 1 + #else + #define BLARGG_LITTLE_ENDIAN 1 + #endif +#endif + +// Determine compiler's language support + +// Metrowerks CodeWarrior +#if defined (__MWERKS__) + #define BLARGG_COMPILER_HAS_NAMESPACE 1 + #if !__option(bool) + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #define STATIC_CAST(T,expr) static_cast< T > (expr) + +// Microsoft Visual C++ +#elif defined (_MSC_VER) + #if _MSC_VER < 1100 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + +// GNU C++ +#elif defined (__GNUC__) + #if __GNUC__ > 2 + #define BLARGG_COMPILER_HAS_NAMESPACE 1 + #endif + +// Mingw +#elif defined (__MINGW32__) + // empty + +// Pre-ISO C++ compiler +#elif __cplusplus < 199711 + #ifndef BLARGG_COMPILER_HAS_BOOL + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + +#endif + +/* BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compilers. + If errors occur here, add the following line to your config.h file: + #define BLARGG_COMPILER_HAS_BOOL 0 +*/ +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL + typedef int bool; + const bool true = 1; + const bool false = 0; +#endif + +// BLARGG_USE_NAMESPACE: If 1, use headers rather than +#if BLARGG_USE_NAMESPACE || (!defined (BLARGG_USE_NAMESPACE) && BLARGG_COMPILER_HAS_NAMESPACE) + #include + #include + #include + #include + #define STD std +#else + #include + #include + #include + #include + #define STD +#endif + +// BLARGG_NEW is used in place of 'new' to create objects. By default, plain new is used. +// To prevent an exception if out of memory, #define BLARGG_NEW new (std::nothrow) +#ifndef BLARGG_NEW + #define BLARGG_NEW new +#endif + +// BOOST::int8_t etc. + +// HAVE_STDINT_H: If defined, use for int8_t etc. +#if defined (HAVE_STDINT_H) + #include + #define BOOST + +// HAVE_INTTYPES_H: If defined, use for int8_t etc. +#elif defined (HAVE_INTTYPES_H) + #include + #define BOOST + +#else + struct BOOST + { + #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F + typedef signed char int8_t; + typedef unsigned char uint8_t; + #else + // No suitable 8-bit type available + typedef struct see_blargg_common_h int8_t; + typedef struct see_blargg_common_h uint8_t; + #endif + + #if USHRT_MAX == 0xFFFF + typedef short int16_t; + typedef unsigned short uint16_t; + #else + // No suitable 16-bit type available + typedef struct see_blargg_common_h int16_t; + typedef struct see_blargg_common_h uint16_t; + #endif + + #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 + // No suitable 32-bit type available + typedef struct see_blargg_common_h int32_t; + typedef struct see_blargg_common_h uint32_t; + #endif + }; +#endif + +// BLARGG_SOURCE_BEGIN: Library sources #include this after other #includes. +#ifndef BLARGG_SOURCE_BEGIN + #define BLARGG_SOURCE_BEGIN "blargg_source.h" +#endif + +// BLARGG_ENABLE_OPTIMIZER: Library sources #include this for speed-critical code +#ifndef BLARGG_ENABLE_OPTIMIZER + #define BLARGG_ENABLE_OPTIMIZER "blargg_common.h" +#endif + +// BLARGG_CPU_*: Used to select between some optimizations +#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 + +// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +#ifndef BOOST_STATIC_ASSERT + #ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / ((expr) ? 1 : 0) - 1] ) + #else + // Some other compilers fail when declaring same function multiple times in class, + // so differentiate them by line + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / ((expr) ? 1 : 0) - 1] [__LINE__] ) + #endif +#endif + +// STATIC_CAST(T,expr): Used in place of static_cast (expr) +#ifndef STATIC_CAST + #define STATIC_CAST(T,expr) ((T) (expr)) +#endif + +// blargg_err_t (NULL on success, otherwise error string) +#ifndef blargg_err_t + typedef const char* blargg_err_t; +#endif +const char* const blargg_success = 0; + +// blargg_vector: Simple array that does *not* work for types with a constructor (non-POD). +template +class blargg_vector { + T* begin_; + STD::size_t size_; +public: + blargg_vector() : begin_( 0 ), size_( 0 ) { } + ~blargg_vector() { STD::free( begin_ ); } + + typedef STD::size_t size_type; + + blargg_err_t resize( size_type n ) + { + void* p = STD::realloc( begin_, n * sizeof (T) ); + if ( !p && n ) + return "Out of memory"; + begin_ = (T*) p; + size_ = n; + return 0; + } + + void clear() + { + void* p = begin_; + begin_ = 0; + size_ = 0; + STD::free( p ); + } + + size_type size() const { return size_; } + + T* begin() { return begin_; } + T* end() { return begin_ + size_; } + + const T* begin() const { return begin_; } + const T* end() const { return begin_ + size_; } + + T& operator [] ( size_type n ) + { + assert( n <= size_ ); // allow for past-the-end value + return begin_ [n]; + } + + const T& operator [] ( size_type n ) const + { + assert( n <= size_ ); // allow for past-the-end value + return begin_ [n]; + } +}; + +#endif + diff -bNaHudr bla1/blarg-sound/nes_apu/blargg_source.h bla2/blarg-sound/nes_apu/blargg_source.h --- bla1/blarg-sound/nes_apu/blargg_source.h 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/nes_apu/blargg_source.h 2006-10-15 13:40:43.000000000 +0300 @@ -0,0 +1,76 @@ + +// By default, #included at beginning of library source files. +// Can be overridden by #defining BLARGG_SOURCE_BEGIN to path of alternate file. + +// 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 + +// 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( 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 +#ifdef BLARGG_DPRINTF + #define dprintf BLARGG_DPRINTF +#else + inline void blargg_dprintf_( const char*, ... ) { } + #define dprintf (1) ? (void) 0 : blargg_dprintf_ +#endif + +// 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 +#ifdef BLARGG_CHECK + #define check( expr ) BLARGG_CHECK( expr ) +#else + #define check( expr ) ((void) 0) +#endif + +// 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) == 0 ) 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 +inline T min( T x, T y ) +{ + if ( x < y ) + return x; + return y; +} + +template +inline T max( T x, T y ) +{ + if ( x < y ) + return y; + return x; +} + +#endif + diff -bNaHudr bla1/blarg-sound/notes.txt bla2/blarg-sound/notes.txt --- bla1/blarg-sound/notes.txt 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/notes.txt 2005-10-03 15:22:08.000000000 +0300 @@ -0,0 +1,169 @@ +Nes_Snd_Emu 0.1.7 Notes +----------------------- + +Overview +-------- +The library includes a NES APU sound chip emulator, sound sample buffer, +support for state snapshots, a Namco 106 sound chip emulator, a Konami VRC6 +sound chip emulator, and a nonlinear sound buffer. + +The sound chip emulators handle reads and writes to their registers and +generate samples into one or more sound buffers. Register accesses take a CPU +clock count, relative to the current time frame. When a time frame is ended all +samples from it are added to the sound buffer. + +The sound buffer accumulates samples generated by the sound chips and allows +them to be read out at any time. The sample rate can be adjusted freely. + + +Using the APU +------------- +Simple_Apu is recommended instead of Nes_Apu when using the library for the +first time (see Simple_Apu.h for reference). Its source code demonstrates basic +use of the APU. + +To use Nes_Apu (or the other sound chips), its output must be routed to a +Blip_Buffer. Then pass CPU reads and writes to the sound chip, end its time +frame periodically and read samples from the sound buffer. + +Nes_Apu apu; +Blip_Buffer buf; + + Setup +buf.sample_rate() Set sample rate and allocate memory for buffer +buf.clock_rate() Set clock rate (1789773 for NTSC NES) +apu.output() Route APU output to buffer +apu.dmc_reader() Set function APU calls to read memory + + Emulation +apu.write_register() Emulate write to APU register +apu.read_status() Emulate reads APU status register +apu.end_frame() Generate samples for time frame +buf.end_frame() Make samples for time frame available for reading +buf.samples_avail() Number of samples in sound buffer +buf.read_samples() Read samples out of sound buffer + + +Nonlinear Sound Emulation +------------------------- +Nonlinear_Buffer is a special sound buffer which emulates the non-linearity of +the NES APU sound chip on the triangle, noise, and DMC channels. It can be used +in place of Blip_Buffer, except that the APU should be assigned using +buf.enable_nonlinearity( apu ), and other sound chips using +other_sound_chip.output( buf.buffer() ). + + +Blip_Buffer +----------- +Basic Blip_Buffer operation is covered. Further features like frequency +equalization and other features are not described. For more information and +examples of using Blip_Buffer, download the full Blip_Buffer library package + + +Emulation Accuracy +------------------ +Nes_Apu accuracy has some room for improvement, especially regarding IRQ +handling. I am still working on reverse-engineering the NES APU and designing +an improved APU. + +Nes_Namco and Nes_Vrc6 accuracy is based on the documentation I have. I have +made many tweaks to make them sound similar to a couple of samples I've heard +of the real thing. + + +Solving Problems +---------------- +If you're having problems, check the following: + +- If multiple threads are being used, ensure that only one at a time is +accessing objects from the library. This library is not thread-safe. + +- Turn the compiler's optimizer is off. Sometimes an optimizer generates bad +code. + +- Enable debugging support. This enables assertions and other run-time checks. + +- See if the demo works. If not, contact me. + + +Error handling +-------------- +Functions which can fail have a return type of blargg_err_t, which is a pointer +to an error string (const char*). If the function is successful it returns +blargg_success (NULL), otherwise it returns a pointer to an error string. + +To allow compatibility with older C++ compilers, no exceptions are thrown by +any of the modules. The library is exception-safe, and any exceptions which +occur are not intercepted. Any exceptions thrown by the standard library or +caller-supplied functions are not caught. + +The library uses BLARGG_NEW instead of new for allocations. By default, +BLARGG_NEW is #defined to new (nothrow) so that a failed allocation won't cause +an exception. This can be overriden in your config.h file (see below). + +Significant violations of the documented interface are flagged with debug-only +assertions. Failure of these usually indicates a caller error rather than a +defect in the library. + + +Configuration +------------- +The header "blargg_common.h" is used to establish a common environment, and is +#included at the beginning of all library headers and sources. It attempts to +automatically determine the features of the environment, but might need help. + +If HAVE_CONFIG_H is defined in the compiler options, the file "config.h" is +included at the beginning of each library header file, allowing configuration +options for the library to be set. It's fine if other libraries also use this +scheme, as they won't conflict. + +I have attempted to design the library so that configuration can be done +without modifying any of the library sources and header files. This makes it +easy to upgrade to a new version without losing any customizations to its +configuration. + +Some parts of the library might depend on the order of bytes in multibyte +types. If so, they will cause a compilation error if the order can't be +determined (rather than silently fail). If this occurs, define the appropriate +symbol. For big-endian (most significant byte first, i.e. Motorola 68000, +PowerPC), #define BLARGG_BIG_ENDIAN to 1. For little-endian (least significant +byte first, i.e. Intel x86), #define BLARGG_LITTLE_ENDIAN to 1. + +Older C++ compilers might not support bool. Support is provided where bool is +not available, but the compiler's support of bool might not be properly +determined. If errors occur in "blargg_common.h" in the bool section, #define +BLARGG_COMPILER_HAS_BOOL to 1 if your compiler supports bool, otherwise 0. + +If your compiler doesn't support static_cast<>, #define STATIC_CAST( type ) to +(type) so that old-style casts are used instead. + +If your compiler supports namespaces, blargg_common.h uses standard headers +with the "c" prefix to avoid bringing names from std into the global namespace. +If your compiler supports namespaces but this isn't being detected by +blargg_common.h, #define BLARGG_COMPILER_HAS_NAMESPACE to 1. + +Every library source file has #include BLARGG_SOURCE_BEGIN after all other +#include lines, to allow setting compiler options and disabling warnings for +library sources only. By default, this is #defined to "blargg_source.h". + +In library source files, #include BLARGG_ENABLE_OPTIMIZER is used before +performance-critical code. By default it doesn't do anything, since enabling +compiler options involves compiler-specific #pragma directives. + +After you've got the library working, you can enable platform-specific +optimizations by #defining BLARGG_NONPORTABLE to 1. Contact me if you encounter +any problems that only occur when these are enabled. + +If you have any problems with "blargg_common.h", contact me. + + +Boost Compatibility +------------------- +Boost is a collection of small, useful C++ libraries which provide basic +services. If it's not installed in your environment or your environment isn't +supported, a tiny substitute is included in the "boost/" directory. This +implements only that boost functionality used, and is written to work with most +compilers. If boost is already installed, you can delete the included "boost/" +directory. For more information about boost, see http://boost.org/ + + diff -bNaHudr bla1/blarg-sound/readme.txt bla2/blarg-sound/readme.txt --- bla1/blarg-sound/readme.txt 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/readme.txt 2005-10-03 16:07:32.000000000 +0300 @@ -0,0 +1,66 @@ +Nes_Snd_Emu 0.1.7: NES Sound Emulator +------------------------------------- +This is a portable Nintendo Entertainment System (NES) 2A03 APU sound chip +emulator library for use in a NES emulator. Its main features are high +accuracy, sound quality, and efficiency. Also included are Namco 106 and Konami +VRC6 expansion sound chip emulators. + +Licensed under the GNU Lesser General Public License (LGPL); see LGPL.TXT. +Copyright (C) 2003-2005 Shay Green. + +Website: http://www.slack.net/~ant/libs/ +Forum : http://groups-beta.google.com/group/blargg-sound-libs +Contact: hotpop.com@blargg (swap to e-mail) + + +Getting Started +--------------- +Build a program consisting of demo.cpp, Simple_Apu.cpp, and all source files in +the nes_apu/ directory. Running the program should generate a WAVE sound file +"out.wav" of random tones. + +See notes.txt for more information, and respective header (.h) files for +reference. Visit the discussion forum to get assistance. + + +Files +----- +notes.txt General notes about the library +changes.txt Changes since previous releases +LGPL.TXT GNU Lesser General Public License + +usage.txt Two ways of using Nes_Apu in a NES emulator +demo.cpp Shows how to use Simple_Apu in an emulator +Simple_Apu.h Simpler interface for APU for getting started +Simple_Apu.cpp +Wave_Writer.hpp WAVE sound file writer used for demo output +Wave_Writer.cpp +Sound_Queue.h Synchronous sound queue for use with SDL sound +Sound_Queue.cpp + +nes_apu/ Core library modules + Nes_Apu.h NES APU emulator + Nes_Apu.cpp + Nes_Oscs.h + Nes_Oscs.cpp + blargg_common.h Common services + blargg_source.h + Blip_Synth.h Sound synthesis buffer + Blip_Buffer.h + Blip_Buffer.cpp + Optional modules + apu_snapshot.cpp Snapshot support + apu_snapshot.h + Nes_Vrc6.h Konami VRC6 sound chip emulator + Nes_Vrc6.cpp + Nes_Namco.h Namco 106 sound chip emulator + Nes_Namco.cpp + Nonlinear_Buffer.h Sample buffer that emulates APU's non-linearity + Nonlinear_Buffer.cpp + Multi_Buffer.h + Multi_Buffer.cpp + +boost/ Substitute for boost library if it's unavailable + +-- +Shay Green (swap to e-mail) diff -bNaHudr bla1/blarg-sound/usage.txt bla2/blarg-sound/usage.txt --- bla1/blarg-sound/usage.txt 1970-01-01 02:00:00.000000000 +0200 +++ bla2/blarg-sound/usage.txt 2005-10-03 14:00:42.000000000 +0300 @@ -0,0 +1,234 @@ + +// Using the APU in an emulator +// ---------------------------- +// +// Two code skeletons are shown below. The first shows how to add basic APU support to an +// emulator that doesn't keep track of overall CPU time. The second shows how to add full +// APU support (including IRQs) to a framework which keeps track of overall CPU time. + +// Basic APU support +// ----------------- + +#include "Nes_Apu.h" + +Blip_Buffer buf; +Nes_Apu apu; + +void output_samples( const blip_sample_t*, size_t count ); +const size_t out_size = 4096; +blip_sample_t out_buf [out_size]; + +int total_cycles; +int cycles_remain; + +int elapsed() +{ + return total_cycles - cycles_remain; +} + +const int apu_addr = 0x4000; + +void cpu_write_memory( cpu_addr_t addr, int data ) +{ + // ... + if ( addr >= apu.start_addr && addr <= apu.end_addr ) + apu.write_register( elapsed(), addr, data ); +} + +int cpu_read_memory( cpu_addr_t addr ) +{ + // ... + if ( addr == apu.status_addr ) + return apu.read_status( elapsed() ); +} + +int dmc_read( void*, cpu_addr_t addr ) +{ + return cpu_read_memory( addr ); +} + +void emulate_cpu( int cycle_count ) +{ + total_cycles += cycle_count; + cycles_remain += cycle_count; + + while ( cycles_remain > 0 ) + { + // emulate opcode + // ... + cycles_remain -= cycle_table [opcode]; + } +} + +void end_time_frame( int length ) +{ + apu.end_frame( length ); + buf.end_frame( length ); + total_cycles -= length; + + // Read some samples out of Blip_Buffer if there are enough to + // fill our output buffer + if ( buf.samples_avail() >= out_size ) + { + size_t count = buf.read_samples( out_buf, out_size ); + output_samples( out_buf, count ); + } +} + +void render_frame() +{ + // ... + end_time_frame( elapsed() ); +} + +void init() +{ + blargg_err_t error = buf.sample_rate( 44100 ); + if ( error ) + report_error( error ); + buf.clock_rate( 1789773 ); + apu.output( &buf ); + + apu.dmc_reader( dmc_read ); +} + + +// Full APU support +// ---------------- + +#include "Nes_Apu.h" + +Blip_Buffer buf; +Nes_Apu apu; + +void output_samples( const blip_sample_t*, size_t count ); +const size_t out_size = 4096; +blip_sample_t out_buf [out_size]; + +cpu_time_t cpu_end_time; // Time for CPU to stop at +cpu_time_t cpu_time; // Current CPU time relative to current time frame + +unsigned apu_addr = 0x4000; + +void cpu_write_memory( cpu_addr_t addr, int data ) +{ + // ... + if ( addr >= apu.start_addr && addr <= apu.end_addr ) + apu.write_register( cpu_time, addr, data ); +} + +int cpu_read_memory( cpu_addr_t addr ) +{ + // ... + if ( addr == apu.status_addr ) + return apu.read_status( cpu_time ); +} + +int dmc_read( void*, cpu_addr_t addr ) +{ + return cpu_read_memory( addr ); +} + +void emulate_cpu() +{ + while ( cpu_time < cpu_end_time ) + { + // Decode instruction + // ... + cpu_time += cycle_table [opcode]; + switch ( opcode ) + { + // ... + case 0x58: // CLI + if ( cpu_status & i_flag ) + { + cpu_status &= ~i_flag; + return; // I flag cleared; stop CPU immediately + } + } + } +} + +// Time of next IRQ if before end_time, otherwise end_time +cpu_time_t earliest_irq_before( cpu_time_t end_time ) +{ + if ( !(cpu_status & i_flag) ) + { + cpu_time_t irq_time = apu.earliest_irq(); + if ( irq_time < end_time ) + end_time = irq_time; + } + return end_time; +} + +// IRQ time may have changed, so update CPU end time +void irq_changed( void* ) +{ + cpu_end_time = earliest_irq_before( cpu_end_time ); +} + +// Run CPU to 'end_time' (possibly a few cycles over depending on instruction) +void run_cpu_until( cpu_time_t end_time ) +{ + while ( cpu_time < end_time ) + { + cpu_end_time = earliest_irq_before( end_time ); + if ( cpu_end_time <= cpu_time ) + { + // Save PC and status, load IRQ vector, set I flag, etc. + cpu_trigger_irq(); + + // I flag is now set, so CPU can be run for full time + cpu_end_time = end_time; + } + + emulate_cpu(); + } +} + +// Run CPU for at least 'cycle_count' +void run_cpu( int cycle_count ) +{ + run_cpu_until( cpu_time + cycle_count ); +} + +// End a time frame and make its samples available for reading +void end_time_frame( cpu_time_t length ) +{ + apu.end_frame( length ); + buf.end_frame( length ); + cpu_time -= length; + + // Read some samples out of Blip_Buffer if there are enough to + // fill our output buffer + if ( buf.samples_avail() >= out_size ) + { + size_t count = buf.read_samples( out_buf, out_size ); + output_samples( out_buf, count ); + } +} + +// Emulator probably has a function which renders a video frame +void render_video_frame() +{ + for ( int n = scanline_count; n--; ) + { + run_cpu( 113 ); // or whatever would be done here + // ... + } + // ... + end_time_frame( cpu_time ); +} + +void init() +{ + blargg_err_t error = buf.sample_rate( 44100 ); + if ( error ) + report_error( error ); + buf.clock_rate( 1789773 ); + apu.output( &buf ); + + apu.dmc_reader( dmc_read ); + apu.irq_notifier( irq_changed ); +} + diff -bNaHudr bla1/boards/n106.c bla2/boards/n106.c --- bla1/boards/n106.c 2005-12-01 04:05:12.000000000 +0200 +++ bla2/boards/n106.c 2007-06-07 19:53:28.000000000 +0300 @@ -19,6 +19,7 @@ */ #include "mapinc.h" +#include "../blarg-sound/BlarggApu.h" static uint16 IRQCount; static uint8 IRQa; @@ -28,11 +29,13 @@ static DECLFR(AWRAM) { + BlarggRead(); return(WRAM[A-0x6000]); } static DECLFW(BWRAM) { + BlarggWrite(A, V); WRAM[A-0x6000]=V; } @@ -86,6 +89,7 @@ static DECLFR(Namco_Read4800) { + BlarggRead(); uint8 ret=IRAM[dopol&0x7f]; /* Maybe I should call NamcoSoundHack() here? */ if(!fceuindbg) @@ -96,11 +100,13 @@ static DECLFR(Namco_Read5000) { + BlarggRead(); return(IRQCount); } static DECLFR(Namco_Read5800) { + BlarggRead(); return(IRQCount>>8); } @@ -168,6 +174,9 @@ static DECLFW(Mapper19_write) { A&=0xF800; + + BlarggWrite(A, V); + if(A>=0x8000 && A<=0xb800) DoCHRRAMROM((A-0x8000)>>11,V); else switch(A) @@ -387,6 +396,7 @@ void NSFN106_Init(void) { + BlarggEnableNamco(); SetWriteHandler(0xf800,0xffff,Mapper19_write); SetWriteHandler(0x4800,0x4fff,Mapper19_write); SetReadHandler(0x4800,0x4fff,Namco_Read4800); @@ -398,6 +408,7 @@ static void N106_Power(void) { int x; + BlarggEnableNamco(); SetReadHandler(0x8000,0xFFFF,CartBR); SetWriteHandler(0x8000,0xffff,Mapper19_write); SetWriteHandler(0x4020,0x5fff,Mapper19_write); @@ -441,6 +452,8 @@ if(FSettings.SndRate) Mapper19_ESI(); + BlarggEnableNamco(); + AddExState(WRAM, 8192, 0, "WRAM"); AddExState(IRAM, 128, 0, "WRAM"); AddExState(N106_StateRegs, ~0, 0, 0); diff -bNaHudr bla1/fceu.c bla2/fceu.c --- bla1/fceu.c 2007-06-07 20:35:27.000000000 +0300 +++ bla2/fceu.c 2007-06-07 20:35:22.000000000 +0300 @@ -55,6 +55,8 @@ #include "drivers/win/basicbot.h" #endif +#include "BlarggApu.h" + uint64 timestampbase; @@ -513,6 +515,12 @@ *SoundBuf=WaveFinal; *SoundBufSize=ssize; +#if 1 /* set to 0 to disable Blargg sound, nonzero to enable */ + *SoundBufSize = BlarggGetSamplesAvail(); + *SoundBuf = (int32*)BlarggGetSoundBuffer(); + BlarggEndFrame(); +#endif + if(EmulationPaused&2) { EmulationPaused = 1; // restore paused flag diff -bNaHudr bla1/mappers/24and26.c bla2/mappers/24and26.c --- bla1/mappers/24and26.c 2005-10-30 18:26:34.000000000 +0200 +++ bla2/mappers/24and26.c 2006-11-10 21:16:09.000000000 +0200 @@ -20,6 +20,8 @@ #include "mapinc.h" +#include "../blarg-sound/BlarggApu.h" + static void (*sfun[3])(void); #define vrctemp mapbyte1[0] @@ -59,16 +61,19 @@ A&=0xF003; if(A>=0x9000 && A<=0x9002) { + BlarggWrite(A, V); VPSG[A&3]=V; if(sfun[0]) sfun[0](); } else if(A>=0xa000 && A<=0xa002) { + BlarggWrite(A, V); VPSG[4|(A&3)]=V; if(sfun[1]) sfun[1](); } else if(A>=0xb000 && A<=0xb002) { + BlarggWrite(A, V); VPSG2[A&3]=V; if(sfun[2]) sfun[2](); } @@ -348,6 +353,8 @@ void Mapper24_init(void) { + fprintf(stderr, "*** VRC6 ***\n"); + BlarggEnableVRC6(); SetWriteHandler(0x8000,0xffff,Mapper24_write); VRC6_ESI(); MapIRQHook=KonamiIRQHook; @@ -356,6 +363,8 @@ void Mapper26_init(void) { + fprintf(stderr, "*** VRC6 ***\n"); + BlarggEnableVRC6(); SetWriteHandler(0x8000,0xffff,Mapper24_write); VRC6_ESI(); MapIRQHook=KonamiIRQHook; @@ -364,6 +373,8 @@ void NSFVRC6_Init(void) { + fprintf(stderr, "*** VRC6 ***\n"); + BlarggEnableVRC6(); VRC6_ESI(); SetWriteHandler(0x8000,0xbfff,VRC6SW); } diff -bNaHudr bla1/mappers/69.c bla2/mappers/69.c --- bla1/mappers/69.c 2005-10-13 23:37:10.000000000 +0300 +++ bla2/mappers/69.c 2006-08-18 00:08:44.000000000 +0300 @@ -20,6 +20,8 @@ #include "mapinc.h" +#include "../blarg-sound/BlarggApu.h" + static void AYSound(int Count); static void AYSoundHQ(void); static void DoAYSQ(int x); @@ -44,11 +46,13 @@ static DECLFW(Mapper69_SWL) { + BlarggWrite(0xC000, V); sunindex=V%14; } static DECLFW(Mapper69_SWH) { +BlarggWrite(0xE000, V); int x; GameExpSound.Fill=AYSound; GameExpSound.HiFill=AYSoundHQ; @@ -133,6 +137,7 @@ CAYBC[x]=end; if(amp) + if(!(MapperExRAM[0x7]&(1<>6; fcnt=0; if(V&0x2) @@ -1192,6 +1201,8 @@ void FCEUI_Sound(int Rate) { + BlarggSetSampleRate(Rate); + FSettings.SndRate=Rate; SetSoundVariables(); } diff -bNaHudr bla1/x6502.c bla2/x6502.c --- bla1/x6502.c 2007-06-07 20:33:05.000000000 +0300 +++ bla2/x6502.c 2007-06-07 20:36:13.000000000 +0300 @@ -25,6 +25,8 @@ #include "fceu.h" #include "sound.h" +#include "BlarggApu.h" + X6502 X; #ifdef FCEUDEF_DEBUGGER @@ -465,6 +467,7 @@ cycles*=16; // 16*4=64 _count+=cycles; + BlarggWillExecute(cycles); while(_count>0) { @@ -602,6 +605,7 @@ cycles*=16; // 16*4=64 _count+=cycles; + BlarggWillExecute(cycles); while(_count>0) {