All Projects → jamesathey → Nes_Snd_Emu

jamesathey / Nes_Snd_Emu

Licence: LGPL-2.1 license
NES / Famicom sound library, descended from blargg's Nes_Snd_Emu

Programming Languages

C++
36643 projects - #6 most used programming language
CMake
9771 projects
c
50402 projects - #5 most used programming language

Projects that are alternatives of or similar to Nes Snd Emu

cfxnes
JavaScript NES emulator and emulation library.
Stars: ✭ 56 (+194.74%)
Mutual labels:  emulation, nes, nintendo-nes
Dn-FamiTracker
modifications and improvements for 0CC-FamiTracker (based on j0CC-FamiTracker 0.6.3)
Stars: ✭ 126 (+563.16%)
Mutual labels:  nes, famicom, chiptune
Donsol
Card game for the Famicom, written in 6502 assembly
Stars: ✭ 106 (+457.89%)
Mutual labels:  nes, famicom
FCEUX-Lua-Script-Collection
Lua scripts for FCEUX (an NES emulator). Multiple games including River City Ransom, Battle Kid
Stars: ✭ 20 (+5.26%)
Mutual labels:  emulation, nes
Provenance
iOS & tvOS multi-emulator frontend, supporting various Atari, Bandai, NEC, Nintendo, Sega, SNK and Sony console systems… Get Started: https://wiki.provenance-emu.com |
Stars: ✭ 4,732 (+24805.26%)
Mutual labels:  emulation, nes
SaltyNES
A NES emulator in WebAssembly
Stars: ✭ 69 (+263.16%)
Mutual labels:  nes, nintendo-nes
first nes
Create your own games for the Nintendo Entertainment System! This "starter" game is easily extensible for your own projects. Includes references.
Stars: ✭ 94 (+394.74%)
Mutual labels:  nes, nintendo-nes
js-nes-emulator
NES emulator in javascript.
Stars: ✭ 12 (-36.84%)
Mutual labels:  emulation, nes
Jsnes Web
A browser UI for JSNES, a JavaScript NES emulator
Stars: ✭ 398 (+1994.74%)
Mutual labels:  emulation, nes
Jsnes
A JavaScript NES emulator.
Stars: ✭ 5,354 (+28078.95%)
Mutual labels:  emulation, nes
Visualnes
Visual NES simulates the CPU & PPU of a NES at the transistor level.
Stars: ✭ 55 (+189.47%)
Mutual labels:  emulation, nes
asm6f
A fork of loopy's ASM6, a 6502 assembler.
Stars: ✭ 79 (+315.79%)
Mutual labels:  nes, famicom
Gearnes
NES / Famicom emulator for iOS, Mac, Raspberry Pi, Windows, Linux and RetroArch.
Stars: ✭ 23 (+21.05%)
Mutual labels:  nes, famicom
Punes
Nintendo Entertaiment System emulator and NSF/NSFe Music Player (Linux, FreeBSD, OpenBSD and Windows)
Stars: ✭ 217 (+1042.11%)
Mutual labels:  emulation, nes
Nsfplay
Nintendo NES sound file NSF music player
Stars: ✭ 155 (+715.79%)
Mutual labels:  emulation, nes
SwiftNES
Cross-platform NES Emulator written in Swift
Stars: ✭ 29 (+52.63%)
Mutual labels:  nes, famicom
stg-for-nes
Simple STG for NES
Stars: ✭ 18 (-5.26%)
Mutual labels:  nes, famicom
Emulator.NES
📺 Nintendo Entertainment System emulator written in C#.
Stars: ✭ 123 (+547.37%)
Mutual labels:  nes, famicom
Anese
Another NES Emulator - written for fun & learning - first implementation of wideNES
Stars: ✭ 323 (+1600%)
Mutual labels:  emulation, nes
Droidsound
Droidsound-E. Download links in README.md
Stars: ✭ 136 (+615.79%)
Mutual labels:  emulation, nes

Nes_Snd_Emu: NES Sound Emulator

Actions Status

Nes_Snd_Emu is a portable Nintendo Entertainment System (NES) 2A03/2A07 APU sound chip emulator library. Its main features are high accuracy, sound quality, and efficiency. Also included are emulators for the following Famicom expansion sound chips:

  • Konami VRC6
  • Konami VRC7
  • Namco 163
  • Nintendo Famicom Disk System (FDS)
  • Nintendo MMC5
  • Sunsoft 5B

The library also includes a sound sample buffer, support for state snapshots, and a nonlinear sound buffer.

Licensed under the GNU Lesser General Public License (LGPL).

Build Requirements

  • CMake 3.0+
  • A C++11 compiler
  • The optional Sound_Queue class uses libSDL.

Previous versions of Nes_Snd_Emu went to great lengths to support obsolete platforms and compilers. The current maintainer does not have these obsolete targets to test against, and quality C++ compilers are available for free on every modern platform. Therefore, support for obsolete targets has been removed.

Technical Overview

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.

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()
{
	std::error_condition 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( 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()
{
	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()
{
	std::error_condition 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;
}

Emulation Accuracy

Nes_Apu accuracy has some room for improvement, especially regarding IRQ handling.

Much of the expansion audio emulation code is based on potentially outdated documentation. A thorough review of these chips using the latest discoveries and documentation is pending.

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.
  • Enable debugging support. This enables assertions and other run-time checks.
  • See if the demo works.

Error handling

Functions which can fail have a return type of std::error_condition, which is a portable error construct included with C++11 and later.

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.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].