Emuforums.com

Go Back   Emuforums.com > General Discussion > Web development / Programming
Home Register Downloads FAQ Members List Calendar Arcade Mark Forums Read

WON'T YOU JOIN US?
You are not a registered member and
are viewing this site as a guest.
Registration is simple and FREE.
Join this CrowdGather community today.
Registration offers the following perks:

» Less advertising throughout
» Post and participate in discussions
» Network with other forum members
» Free private messaging

join

Reply
 
Thread Tools Display Modes
Old December 2nd, 2011, 08:02   #81
skvmb
Registered User
 
skvmb's Avatar
 
Join Date: Mar 2011
Location: Missouri
Posts: 4
@refraction

Okay. I see it know. The instructions starting with byte 08. But these would only rotate by multiples of 90 degrees (0 or 360, 90, 180, and 270.)
__________________
skvmb is offline   Reply With Quote

Advertisement [Remove Advertisement]
Old December 2nd, 2011, 09:11   #82
paul_nicholls
Registered User
 
Join Date: Sep 2011
Location: Australia, Tasmania
Posts: 180
And the flip instructions don't rotate at all either...only flip in horizontal or vertical directions...
paul_nicholls is offline   Reply With Quote
Old December 2nd, 2011, 09:27   #83
tykel
Sober coder
 
tykel's Avatar
 
Join Date: Aug 2010
Location: London, UK
Posts: 433
Ok, here we go: spec v1.0 is out. Why 1.0? Because I feel Chip16 has reached a stage where it can be used for non-trivial things now.
See OP for full spec.

New instructions:
Code:
0D 0X LL HH     SNP Rx, HHLL   Play Sound for HHLL miliseconds at the tone specified in address pointed to by Register X (uses current sound generator settings; ADSR, etc.).

0E AD SR VT     SNG AD, VTSR   Sound generator (applies to all sound tones which follow).
                                  A = attack  (0..15)
                                  D = decay   (0..15)
                                  S = sustain (0..15, volume)
                                  R = release (0..15)
                                  V = volume  (0..15)
                                  T = type of sound:
                                     00 = triangle wave
                                     01 = sawtooth wave
                                     02 = pulse wave (is just square for now)
                                     03 = noise
                                     non-valid values default to 0 output

D0 00 LL HH    PAL HHLL   Load the palette starting at address HHLL, 16*3 bytes, RGB format; used for all drawing since last vblank.

D1 0x 00 00    PAL Rx   Load the palette starting at the address pointed to by Register X, 16*3 bytes, RGB format; used for all drawing since last vblank.
Gentlemen, please update your emulators! I will be updating tchip16 ASAP.
__________________
tchip16 (chip16 assembler) Js16 (browser chip16 emulator)
mash16 (chip16 emulator) img16 (chip16 sprite converter)
______________________________________

Desktop: i5 750 @ 3.6 Ghz, 4GB ram, GTX 570 OC | Windows 7 Pro 64
Laptop: (Thinkpad) i5 430M, 4GB ram, Intel IGP | Arch Linux, Windows 7 Pro 64
tykel is offline   Reply With Quote
Old December 2nd, 2011, 09:58   #84
paul_nicholls
Registered User
 
Join Date: Sep 2011
Location: Australia, Tasmania
Posts: 180
Ok, since the updated spec is now out, here is my source code for the sound generation. It can most likely be optimized, but it should be simple enough to convert over to other languages

How to use:
Code:
uses
  // sound generation unit
  unit_SID
  // WAV file format creator
  unit_wavebuffer,
  ;

{...}

const
  cDuration  = 310; //ms
  cFrequency = 100;  //Hz

var
  SID      : TSID_Chip16;
  outstream: TMemoryStream;
  waveform : Integer;
begin
  SID       := TSID_Chip16.Create;
  outstream := TMemoryStream.Create;
  try
    SID.Reset;
    SID.SetSampleRate(44100);
    SID.SetAttack(unit_SID.cAttack_2mSec);
    SID.SetDecay(unit_SID.cDecay_6mSec);
    SID.SetSustain(15);
    SID.SetRelease(unit_SID.cRelease_300mSec);

    // output all 4 wave types as an example
    for waveform := 0 to 3 do
    begin
      SID.SetWaveform(waveform);
      SID.GenerateWaveform(cFrequency,cDuration,outstream);
      // save sound stream to file (would play instead in an emulator)
      outstream.SaveToFile('SID_'+IntToStr(waveform)+'.wav');
    end;
  finally
    SID.Free;
    outstream.Free;
  end;
unit_SID.pas
Code:
unit unit_SID;

interface

uses
  Classes;

const
  cTriangleWave   = $00;
  cSawtoothWave   = $01;
  cPulseWave      = $02;
  cNoiseWave      = $03;

  cAttack_2mSec   = 0;
  cAttack_8mSec   = 1;
  cAttack_16mSec  = 2;
  cAttack_24mSec  = 3;
  cAttack_38mSec  = 4;
  cAttack_56mSec  = 5;
  cAttack_68mSec  = 6;
  cAttack_80mSec  = 7;
  cAttack_100mSec = 8;
  cAttack_250mSec = 9;
  cAttack_500mSec = 10;
  cAttack_800mSec = 11;
  cAttack_1Sec    = 12;
  cAttack_3Sec    = 13;
  cAttack_5Sec    = 14;
  cAttack_8Sec    = 15;

  cAttackDelayLUT : Array[0..15] Of LongWord =
  // miliseconds
  (
    2,
    8,
    16,
    24,
    38,
    56,
    68,
    80,
    100,
    250,
    500,
    800,
    1000,
    3000,
    5000,
    8000
  );
{..............................................................................}
  cDecay_6mSec    = 0;
  cDecay_14mSec   = 1;
  cDecay_48mSec   = 2;
  cDecay_72mSec   = 3;
  cDecay_114mSec  = 4;
  cDecay_168mSec  = 5;
  cDecay_204mSec  = 6;
  cDecay_240mSec  = 7;
  cDecay_300mSec  = 8;
  cDecay_750mSec  = 9;
  cDecay_1500mSec = 10;
  cDecay_2400mSec = 11;
  cDecay_3Sec     = 12;
  cDecay_9Sec     = 13;
  cDecay_15Sec    = 14;
  cDecay_24Sec    = 15;

  cDecayDelayLUT : Array[0..15] Of LongWord =
  // miliseconds
  (
    6,
    24,
    48,
    72,
    114,
    168,
    204,
    240,
    300,
    750,
    1500,
    2400,
    3000,
    9000,
    15000,
    24000
  );
{..............................................................................}
  cRelease_6mSec    = 0;
  cRelease_14mSec   = 1;
  cRelease_48mSec   = 2;
  cRelease_72mSec   = 3;
  cRelease_114mSec  = 4;
  cRelease_168mSec  = 5;
  cRelease_204mSec  = 6;
  cRelease_240mSec  = 7;
  cRelease_300mSec  = 8;
  cRelease_750mSec  = 9;
  cRelease_1500mSec = 10;
  cRelease_2400mSec = 11;
  cRelease_3Sec     = 12;
  cRelease_9Sec     = 13;
  cRelease_15Sec    = 14;
  cRelease_24Sec    = 15;

  cReleaseDelayLUT : Array[0..15] Of LongWord =
  // miliseconds
  (
    6,
    24,
    48,
    72,
    114,
    168,
    204,
    240,
    300,
    750,
    1500,
    2400,
    3000,
    9000,
    15000,
    24000
  );

type
  TADSRMode = (
      adsrAttack,
      adsrDecay,
      adsrSustain,
      adsrRelease,
      adsrIdle
  );

  TReg4  = LongWord;
  TReg8  = LongWord;
  TReg12 = LongWord;
  TReg16 = LongWord;
  TReg24 = LongWord;

  TSID_Chip16 = class
  private
    FAccumulator        : TReg24;
    FShiftRegister      : TReg24;

    // Fout  = (Fn*Fclk/16777216)Hz
    FFreq               : TReg16;

    // The waveform type; used for output function table lookup.
    FWaveform           : TReg8;

    // 12-Bit pulse width (2048 = square wave)
    // PWout = (PWn/40.95)%
    FPulseWidth         : TReg12;

    FSampleRateHz       : LongWord;
    // sample rate time in seconds
    FSampleRateS        : Single;

    // current ADSR mode
    FADSRMode           : TADSRMode;

    // various ADSR counts and values for altering the volume
    FAttackSampleCount  : LongInt;
    FDecaySampleCount   : LongInt;
    FReleaseSampleCount : LongInt;
    FAttackCount        : LongInt;
    FAttackIncrement    : LongInt;
    FDecayCount         : LongInt;
    FDecayIncrement     : LongInt;
    FReleaseCount       : LongInt;
    FReleaseIncrement   : LongInt;

    FAttackDelay        : TReg4;
    FDecayDelay         : TReg4;
    FSustainVolume      : TReg4;
    FReleaseDelay       : TReg4;

    function  GetOutputTriangle: TReg12;
    function  GetOutputSawtooth: TReg12;
    function  GetOutputPulse   : TReg12;
    function  GetOutputNoise   : TReg12;

    procedure CalculateDelays;
    procedure Gate;
    procedure Release;
    procedure UpdateADSR;
    function  GetADSRAmplitude: Single;
  public
    constructor Create;

    procedure Reset;

    procedure SetSampleRate(samplerate: LongWord);
    procedure SetFrequency (frequency: TReg16);
    procedure SetWaveform  (waveform : TReg4);
    procedure SetAttack    (attack   : TReg4);
    procedure SetDecay     (decay    : TReg4);
    procedure SetSustain   (sustain  : TReg4);
    procedure SetRelease   (release  : TReg4);

    procedure Clock;
    function  GetOutput: TReg12;

    procedure GenerateWaveform(frequency,duration: TReg16; WAVFile: TStream);

    property Waveform: TReg8 read FWaveform;
  end;

implementation

uses
  unit_wavbuffer;

constructor TSID_Chip16.Create;
begin
  inherited Create;
  SetSampleRate(44100);

  Reset;
end;

procedure TSID_Chip16.Reset;
begin
  FAccumulator   := 0;
  FShiftRegister := $7ffff8;
  FFreq          := 0;
  FPulseWidth    := 2048; // defaults to square wave

  FAttackDelay  := 0;
  FDecayDelay   := 0;
  FSustainVolume := 0;
  FReleaseDelay := 0;
  CalculateDelays;
end;

procedure TSID_Chip16.CalculateDelays;
var
  v1,v2 : Byte;
begin
  v1 := 0;
  v2 := 15;
  FAttackSampleCount := (cAttackDelayLUT[FAttackDelay] * FSampleRateHz) div 1000;
  FAttackIncrement   := Trunc(65536 * (v2 - v1) / FAttackSampleCount);

  v1 := 15;
  v2 := FSustainVolume;
  FDecaySampleCount := (cDecayDelayLUT[FDecayDelay] * FSampleRateHz) div 1000;
  FDecayIncrement   := Trunc(65536 * (v2 - v1) / FDecaySampleCount);

  v1 := FSustainVolume;
  v2 := 0;
  FReleaseSampleCount := (cReleaseDelayLUT[FReleaseDelay] * FSampleRateHz) div 1000;
  FReleaseIncrement   := Trunc(65536 * (v2 - v1) / FReleaseSampleCount);
end;

function  TSID_Chip16.GetADSRAmplitude: Single;
begin
  case FADSRMode Of
    adsrAttack  : Result := (FAttackCount Shr 16)  / 15;
    adsrDecay   : Result := (FDecayCount Shr 16)   / 15;
    adsrSustain : Result := FSustainVolume         / 15;
    adsrRelease : Result := (FReleaseCount Shr 16) / 15;
    adsrIdle    : Result := 0;
  end;
end;

procedure TSID_Chip16.SetSampleRate(samplerate: LongWord);
begin
  FSampleRateHz := samplerate;
  FSampleRateS  := 1/FSampleRateHz;
end;

procedure TSID_Chip16.SetFrequency(frequency: TReg16);
// scale frequency to the required SID internal frequency
//Fn   = Fout / 0.0609
begin
  FFreq := Trunc((frequency and $ffff) / 0.0609) and $ffff;
end;

procedure TSID_Chip16.SetWaveform(waveform: TReg4);
begin
  FWaveform := waveform and $03;
end;

procedure TSID_Chip16.SetAttack(attack: TReg4);
begin
  FAttackDelay := attack and $0f;
  CalculateDelays;
end;

procedure TSID_Chip16.SetDecay(decay: TReg4);
begin
  FDecayDelay := decay and $0f;
  CalculateDelays;
end;

procedure TSID_Chip16.SetSustain(sustain: TReg4);
begin
  FSustainVolume := sustain and $0f;
  CalculateDelays;
end;

procedure TSID_Chip16.SetRelease(release: TReg4);
begin
  FReleaseDelay := release and $0f;
  CalculateDelays;
end;

// ----------------------------------------------------------------------------
// Clock 1 cycle.
// ----------------------------------------------------------------------------
procedure TSID_Chip16.Clock;
var
  AccumulatorPrev: TReg24;
  bit0           : TReg24;
begin
  AccumulatorPrev := FAccumulator;

  // Calculate new accumulator value (wrapping around due to 24-bit length);
  FAccumulator := (FAccumulator + FFreq) and $ffffff;

  // Shift noise register once for each time accumulator bit 19 is set high.
  if ((AccumulatorPrev and $080000) = 0) and ((FAccumulator and $080000) <> 0) then
  begin
    bit0           := ((FShiftRegister shr 22) xor (FShiftRegister shr 17)) and $1;

    FShiftRegister := FShiftRegister shl 1;
    FShiftRegister := FShiftRegister and $7fffff;
    FShiftRegister := FShiftRegister or bit0;
  end;
end;

// Triangle:
// The upper 12 bits of the accumulator are used.
// The MSB is used to create the falling edge of the triangle by inverting
// the lower 11 bits. The MSB is thrown away and the lower 11 bits are
// left-shifted (half the resolution, full amplitude).
//
function  TSID_Chip16.GetOutputTriangle: TReg12;
var
  msb: TReg24;
begin
  msb := FAccumulator and $800000;

  if msb = 0 then
    Result := not (FAccumulator shr 11) and $fff
  else
    Result := (FAccumulator shr 11) and $fff;
end;

// Sawtooth:
// The output is identical to the upper 12 bits of the accumulator.
//
function  TSID_Chip16.GetOutputSawtooth: TReg12;
begin
  Result := (FAccumulator shr 12) and $fff;
end;

// Pulse:
// The upper 12 bits of the accumulator are used.
// These bits are compared to the pulse width register by a 12 bit digital
// comparator; output is either all one or all zero bits.
//
function  TSID_Chip16.GetOutputPulse: TReg12;
begin
  Result := $000;

  if (FAccumulator shr 12) >= FPulseWIdth then
    Result := $fff;
end;

// Noise:
// The noise output is taken from intermediate bits of a 23-bit shift register
// which is clocked by bit 19 of the accumulator.
//
// Operation: Calculate EOR result, shift register, set bit 0 = result.
//
//                        ----------------------->---------------------
//                        |                                            |
//                   ----EOR----                                       |
//                   |         |                                       |
//                   2 2 2 1 1 1 1 1 1 1 1 1 1                         |
// Register bits:    2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 <---
//                   |   |       |     |   |       |     |   |
// OSC3 bits  :      7   6       5     4   3       2     1   0
//
// Since waveform output is 12 bits the output is left-shifted 4 times.
//
function  TSID_Chip16.GetOutputNoise: TReg12;
begin
  Result :=
    ((FShiftRegister and $400000) shr 11) or
    ((FShiftRegister and $100000) shr 10) or
    ((FShiftRegister and $010000) shr 7) or
    ((FShiftRegister and $002000) shr 5) or
    ((FShiftRegister and $000800) shr 4) or
    ((FShiftRegister and $000080) shr 1) or
    ((FShiftRegister and $000010) shl 1) or
    ((FShiftRegister and $000004) shl 2);
end;

function  TSID_Chip16.GetOutput: TReg12;
begin
  case FWaveform of
    $00 : Result := GetOutputTriangle;
    $01 : Result := GetOutputSawtooth;
    $02 : Result := GetOutputPulse;
    $03 : Result := GetOutputNoise;
  else
    Result := $000; // illegal waveform, so no output
  end;
end;

procedure TSID_Chip16.Gate;
// start ADSR attack section
begin
  FADSRMode          := adsrAttack;
  FAttackCount       := 0;
  FAttackSampleCount := (cAttackDelayLUT[FAttackDelay] * FSampleRateHz) div 1000;
end;

procedure TSID_Chip16.Release;
// start ADSR release section
begin
  if FADSRMode <> adsrSustain then Exit;
  FADSRMode           := adsrRelease;
  FReleaseCount       := FSustainVolume * 65536;
  FReleaseSampleCount := (cReleaseDelayLUT[FReleaseDelay] * FSampleRateHz) div 1000;
end;

procedure TSID_Chip16.UpdateADSR;
begin
    case FADSRMode of
        adsrAttack  : begin
            Inc(FAttackCount,FAttackIncrement);
            Dec(FAttackSampleCount);
            if FAttackSampleCount <= 0 then
            begin
                FADSRMode         := adsrDecay;
                FDecayCount       := 15 * 65536;
                FDecaySampleCount := (cDecayDelayLUT[FDecayDelay] * FSampleRateHz) div 1000;
            end;
        end;
        adsrDecay   : begin
            Inc(FDecayCount,FDecayIncrement);
            Dec(FDecaySampleCount);
            if FDecaySampleCount <= 0 then
                FADSRMode := adsrSustain;
        end;
        adsrRelease : begin
            Inc(FReleaseCount,FReleaseIncrement);
            Dec(FReleaseSampleCount);
            if FReleaseSampleCount <= 0 then
                FADSRMode := adsrIdle;
        end;
    else
    end;
end;

procedure TSID_Chip16.GenerateWaveform(frequency,duration: TReg16; WAVFile: TStream);
const
  cNumberOfChannels = 1;
  cBitsPerChannel   = 8;
  cClockRateHz      = 1e6;             // 1MHz
  cClockRateS       = 1/cClockRateHz;

  cSampleMidPoint   = ($fff div 2) - 1;

type
  TADSRMode = (adsrAttack,adsrDecay,adsrSustain,adsrRelease,adsrIdle);

var
  TotalTime   : Single;     // current value of overall time in waveform generation
  SIDTime     : Single;     // current value of time for clocking the SID chip
  WAVBuffer   : TWAVBuffer; // a WAV file formatted stream to write to
  samplenum   : LongWord;   // current sample number in output WAV file
  sample      : LongInt;
  ADSRMode    : TADSRMode;  // current ADSR envelope mode
  ADSRVolume  : Single;     // current ADSR volume
  ADSRTime    : Single;     // time for any ADSR section
  SustainVol  : Single;     // volume level in sustain section of ADSR
  ReleaseStart: Single;     // the time at which ADSR sustain ends and release begins.
                            // (Calculated from waveform duration and ADSR envelope parameters).
  Vol1,Vol2   : Single;     // start/end volume for ADSR section
begin
  WAVBuffer := TWAVBuffer.Create(WAVFile,duration,cNumberOfChannels,cBitsPerChannel,FSampleRateHz);
  try
    WAVBuffer.Reset;

    SetFrequency(frequency);

    ADSRMode     := adsrAttack;
    ADSRVolume   := 0;
    SustainVol   := FSustainVolume/15;
    ReleaseStart := duration/1000 - cDecayDelayLUT[FReleaseDelay]/1000;

    SIDTime    := 0;
    TotalTime  := 0;
    ADSRTime   := 0;
    Vol1       := 0;
    Vol2       := 1;

    Gate;

    for samplenum := 0 to WAVBuffer.NumberOfSamples - 1 do
    begin
      sample := GetOutput and $fff; //24-Bit

      // write this sample to the WAV stream after converting it to (-1..+1) range
      // and multiplying it by the ADSR volume

      WAVBuffer.WriteSamples([((sample - cSampleMidPoint) / cSampleMidPoint) * GetADSRAmplitude]);

      // update the SID chip
      SIDTime := SIDTime + FSampleRateS;
      // clock the SID chip as many times as required this sample step
      while SIDTime >= cClockRateS do
      begin
        Clock;
        SIDTime := SIDTime - cClockRateS;
      end;

      UpdateADSR;

      if TotalTime >= ReleaseStart then
        Release;

      TotalTime := TotalTime + FSampleRateS;
    end;
  finally
    WAVBuffer.Free;
  end;
end;

end.
unit_wavbuffer.pas
Code:
unit unit_wavbuffer;
{$IFDEF fpc}
{$MODE DELPHI}
{$ENDIF}
{$H+}
interface

uses
    Classes;
    
type
  TWAVBuffer = class
  private
    FStream           : TStream;
    FDuration_mSec    : LongWord;
    FNumberOfChannels : Byte;
    FBitsPerSample    : Byte;
    FSampleRate       : LongWord;
    FNumberOfSamples  : LongWord;
    FDataSize         : LongWord;
  public
    constructor Create(const AStream            : TStream;
                       const ADuration_mSec     : LongWord;
                       const ANumberOfChannels  : Byte;
                       const ABitsPerSample     : Byte;
                       const ASampleRate        : LongWord);
    procedure WriteWAVHeader;

    procedure Reset;
    procedure WriteSamples(const ASamples : array Of Single);

    property NumberOfChannels: Byte     read FNumberOfChannels;
    property NumberOfSamples : LongWord read FNumberOfSamples;
    property SampleRate      : LongWord read FSampleRate;
    property Duration_mSec   : LongWord read FDuration_mSec;
    property BitsPerSample   : Byte     read FBitsPerSample;
  end;

implementation

constructor TWAVBuffer.Create(const AStream            : TStream;
                              const ADuration_mSec     : LongWord;
                              const ANumberOfChannels  : Byte;
                              const ABitsPerSample     : Byte;
                              const ASampleRate        : LongWord);
begin
  FStream           := AStream;
  FDuration_mSec    := ADuration_mSec;
  FNumberOfChannels := ANumberOfChannels;
  FBitsPerSample    := ABitsPerSample;
  FSampleRate       := ASampleRate;

  if not (FNumberOfChannels in[1,2])  then FNumberOfChannels := 1;
  if not (FBitsPerSample    in[8,16]) then FBitsPerSample    := 8;
  FNumberOfSamples  := (FDuration_mSec * FSampleRate) div 1000;
  FDataSize         := (FBitsPerSample shr 3) * FNumberOfChannels * FNumberOfSamples;
end;

procedure TWAVBuffer.WriteWAVHeader;
const
  WAVE_FORMAT_PCM     = 1;
  RiffId : AnsiString = 'RIFF';
  WaveId : AnsiString = 'WAVE';
  FmtId  : AnsiString = 'fmt ';
  DataId : AnsiString = 'data';

type
  TWaveHeader = packed record
    wFormatTag      : Word;     // format type
    nChannels       : Word;     // number of channels (i.e. mono, stereo, etc.)
    nSamplesPerSec  : LongWord; // sample rate
    nAvgBytesPerSec : LongWord; // for buffer estimation
    nBlockAlign     : Word;     // block size of data
    wBitsPerSample  : Word;     // number of bits per sample of mono data
    cbSize          : Word;     // the count in bytes of the size of
  end;
var
  WaveHeader       : TWaveHeader;
  RiffCount        : Integer;
  TempInt          : LongWord;
begin
  // initialize wave header
  WaveHeader.wFormatTag      := WAVE_FORMAT_PCM;
  WaveHeader.nChannels       := FNumberOfChannels;
  WaveHeader.nSamplesPerSec  := FSampleRate;
  WaveHeader.wBitsPerSample  := FBitsPerSample;
  WaveHeader.nBlockAlign     := WaveHeader.nChannels * WaveHeader.wBitsPerSample shr 3;
  WaveHeader.nAvgBytesPerSec := WaveHeader.nSamplesPerSec * WaveHeader.nBlockAlign;
  WaveHeader.cbSize          := 0;

  {Calculate length of sound data and of file data}
  RiffCount := Length(WaveId) + Length(FmtId) + SizeOf(LongWord) +
               SizeOf(TWaveHeader) + Length(DataId) + SizeOf(LongWord) + FDataSize; // file data

  {write out the wave header}
  FStream.Write(RiffId[1] , 4);                   // 'RIFF'
  FStream.Write(RiffCount , SizeOf(LongWord));    // file data size
  FStream.Write(WaveId[1] , Length(WaveId));      // 'WAVE'
  FStream.Write(FmtId[1]  , Length(FmtId));       // 'fmt '

  TempInt := SizeOf(TWaveHeader);
  FStream.Write(TempInt    , SizeOf(LongWord));   // TWaveFormat data size
  FStream.Write(WaveHeader , SizeOf(WaveHeader)); // WaveFormatEx record
  FStream.Write(DataId[1]  , Length(DataId));     // 'data'
  FStream.Write(FDataSize  , SizeOf(LongWord));   // sound data size
end;

procedure TWAVBuffer.Reset;
begin
  FStream.Seek(0,soFromBeginning);
  WriteWAVHeader;
end;

procedure TWAVBuffer.WriteSamples(const aSamples : array of Single);
var
  Sample_8Bit  : Byte;
  Sample_16Bit : SmallInt;
  Sample       : Single;
  i            : Integer;
begin
  for i := 0 To High(ASamples) do
  begin
    Sample := ASamples[i];

    // clip sample to between [-1,+1]
    if      Sample < -1.0 then Sample := -1.0
    else if Sample > +1.0 then Sample := +1.0;

    // write sample to stream
    if FBitsPerSample = 8 then
    begin
      Sample_8Bit := 127 + Trunc(127 * Sample);
      FStream.Write(Sample_8Bit,SizeOf(Sample_8Bit));
    end
    else
    if FBitsPerSample = 16 then
    begin
      Sample_16Bit := Trunc(32767 * Sample);
      FStream.Write(Sample_16Bit,SizeOf(Sample_16Bit));
    end;
  end;
end;

end.
Any questions, just ask!

LOL! Now I will have to make a Chip16 emulator too as I have no excuses anymore!! haha

cheers,
Paul
paul_nicholls is offline   Reply With Quote
Old December 2nd, 2011, 10:11   #85
tykel
Sober coder
 
tykel's Avatar
 
Join Date: Aug 2010
Location: London, UK
Posts: 433
Very nice, thank you very much!
Could you possibly make a couple more sounds/tunes just to see what is possible with this sound generator?
__________________
tchip16 (chip16 assembler) Js16 (browser chip16 emulator)
mash16 (chip16 emulator) img16 (chip16 sprite converter)
______________________________________

Desktop: i5 750 @ 3.6 Ghz, 4GB ram, GTX 570 OC | Windows 7 Pro 64
Laptop: (Thinkpad) i5 430M, 4GB ram, Intel IGP | Arch Linux, Windows 7 Pro 64
tykel is offline   Reply With Quote
Old December 2nd, 2011, 10:13   #86
paul_nicholls
Registered User
 
Join Date: Sep 2011
Location: Australia, Tasmania
Posts: 180
Ok, I will see what I can whip up ASAP

EDIT: I based my SID around the C64 emulator code in the reSID sound emulator...I used the waveform parts, but worked out my own ADSR stuff.

cheers,
Paul

Last edited by paul_nicholls; December 2nd, 2011 at 10:22..
paul_nicholls is offline   Reply With Quote
Old December 6th, 2011, 03:03   #87
paul_nicholls
Registered User
 
Join Date: Sep 2011
Location: Australia, Tasmania
Posts: 180
Hey all,
I have attached the source + .exe of a test program for my Chip16 SID unit (written using Delphi 2010)...

SIDTest.zip (~406KB)

Have a play, I hope you find it useful

You should be able to get a wide range of sounds generated, just experiment.

Oh, I had slightly changed the GenerateWaveform method in the SID unit (in the zip file) so it uses the release delay on the end of the given duration to calculate the total sound duration which makes more sense

cheers,
Paul
paul_nicholls is offline   Reply With Quote
Old December 6th, 2011, 08:36   #88
tykel
Sober coder
 
tykel's Avatar
 
Join Date: Aug 2010
Location: London, UK
Posts: 433
Very nice, thank you very much!
I was going to suggest making some reference sounds to calibrate people's sound emu to, but (assuming this works correctly) we don't seem to need that anymore!

Oh, and just a question, in your program there are separate note and octave types... how do these relate to the "tone" specified in the new spec?
__________________
tchip16 (chip16 assembler) Js16 (browser chip16 emulator)
mash16 (chip16 emulator) img16 (chip16 sprite converter)
______________________________________

Desktop: i5 750 @ 3.6 Ghz, 4GB ram, GTX 570 OC | Windows 7 Pro 64
Laptop: (Thinkpad) i5 430M, 4GB ram, Intel IGP | Arch Linux, Windows 7 Pro 64

Last edited by tykel; December 6th, 2011 at 08:57..
tykel is offline   Reply With Quote
Old December 6th, 2011, 10:51   #89
paul_nicholls
Registered User
 
Join Date: Sep 2011
Location: Australia, Tasmania
Posts: 180
Quote:
Originally Posted by tykel View Post
Very nice, thank you very much!
I was going to suggest making some reference sounds to calibrate people's sound emu to, but (assuming this works correctly) we don't seem to need that anymore!

Oh, and just a question, in your program there are separate note and octave types... how do these relate to the "tone" specified in the new spec?
In my SID test program, it uses the note and octave to get the required note frequency (or tone) from the array (basically a frequency table). For example, the note A, at octave 4 has a frequency of 440Hz

I calculated the note frequencies from a page similar to this:
http://www.phy.mtu.edu/~suits/notefreqs.html

using equations like here:
http://www.phy.mtu.edu/~suits/NoteFreqCalcs.html

cheers,
Paul
paul_nicholls is offline   Reply With Quote
Old December 6th, 2011, 12:44   #90
tykel
Sober coder
 
tykel's Avatar
 
Join Date: Aug 2010
Location: London, UK
Posts: 433
Great, just what I was looking for. Thanks again. (I think some of this Q/A will be useful to prospective emu-makers now I look at it!)
__________________
tchip16 (chip16 assembler) Js16 (browser chip16 emulator)
mash16 (chip16 emulator) img16 (chip16 sprite converter)
______________________________________

Desktop: i5 750 @ 3.6 Ghz, 4GB ram, GTX 570 OC | Windows 7 Pro 64
Laptop: (Thinkpad) i5 430M, 4GB ram, Intel IGP | Arch Linux, Windows 7 Pro 64
tykel is offline   Reply With Quote
Old December 6th, 2011, 19:27   #91
paul_nicholls
Registered User
 
Join Date: Sep 2011
Location: Australia, Tasmania
Posts: 180
No worries tykel
I have gotten so much information from the internet, I like to give back too
paul_nicholls is offline   Reply With Quote
Old December 12th, 2011, 19:33   #92
ben1066
Registered User
 
Join Date: Nov 2010
Location: UK
Posts: 9
I recently started to implement my own chip 16 emulator. I have previously made a chip 8 emulator and wanted something more challenging. Anyway, since it just hit version 1.0 wouldn't it make sense to add a cpuid function of sorts allowing to get the emulators implemented version?
ben1066 is offline   Reply With Quote
Old December 12th, 2011, 22:08   #93
tykel
Sober coder
 
tykel's Avatar
 
Join Date: Aug 2010
Location: London, UK
Posts: 433
Quote:
Originally Posted by ben1066 View Post
I recently started to implement my own chip 16 emulator. I have previously made a chip 8 emulator and wanted something more challenging. Anyway, since it just hit version 1.0 wouldn't it make sense to add a cpuid function of sorts allowing to get the emulators implemented version?
Maybe the emulator authors should just be more clear about what version of the spec they are implementing.
A CPUID function would add unecessary complexity in my opinion. I think that games/demos for the system should use all the features they want, and there should just be an effort to keep the emu's up-to-date.
Also, the big changes to the spec are done now. I think it is unlikely anything major will be coming soon, until there is widespread adoption of spec 1.0 anyway.

What I would find interesting, though, is to define a Chip16 ROM file format, which would contain various metadata -- like the spec version -- and the emulator could then just complain if its version is below the ROM's. Thoughts?
__________________
tchip16 (chip16 assembler) Js16 (browser chip16 emulator)
mash16 (chip16 emulator) img16 (chip16 sprite converter)
______________________________________

Desktop: i5 750 @ 3.6 Ghz, 4GB ram, GTX 570 OC | Windows 7 Pro 64
Laptop: (Thinkpad) i5 430M, 4GB ram, Intel IGP | Arch Linux, Windows 7 Pro 64
tykel is offline   Reply With Quote
Old December 13th, 2011, 02:28   #94
Bill_gates
Linux's worst nightmare..
 
Bill_gates's Avatar
 
Join Date: Feb 2004
Location: USA
Posts: 1,505
Now that there is proper sound im tempted to jump back into the fray and update MSChip16. But Im trying to stop java whoring and get a proper grasp of c++...
__________________
OS: WinXP Professional Service Pack 3
CPU: Intel pentium 4 3.0GHz
Video: Nvidia Geforce 8400GS
Sound: ASUS Xonar DS 7.1 Channels 24-bit 192KHz PCI Interface Audio Card
Memory: 512 MB
HD: [C:] 140.36/449.09 GB
Connection: Marvell Yukon 88E8053 PCI-E Gigabit Ethernet Controller
Bill_gates is offline   Reply With Quote
Old December 13th, 2011, 03:32   #95
tykel
Sober coder
 
tykel's Avatar
 
Join Date: Aug 2010
Location: London, UK
Posts: 433
So for a potential ROM format, here is an initial suggestion:

Code:
0000h: 'CH16' = magic number
0004h: Spec version (3-char ASCII, eg '1.0' or '0.7')
0007h: Reserved
0008h: rom length (excl. header)
000Ch: start address (defaults to 0)
0010h: MD5 checksum (excl. header)
0020h: Start of binary
__________________
tchip16 (chip16 assembler) Js16 (browser chip16 emulator)
mash16 (chip16 emulator) img16 (chip16 sprite converter)
______________________________________

Desktop: i5 750 @ 3.6 Ghz, 4GB ram, GTX 570 OC | Windows 7 Pro 64
Laptop: (Thinkpad) i5 430M, 4GB ram, Intel IGP | Arch Linux, Windows 7 Pro 64
tykel is offline   Reply With Quote
Old December 13th, 2011, 04:17   #96
paul_nicholls
Registered User
 
Join Date: Sep 2011
Location: Australia, Tasmania
Posts: 180
I like this idea, and the potential ROM format...but why have a reserved part - to make it more official looking? LOL
paul_nicholls is offline   Reply With Quote
Old December 13th, 2011, 04:18   #97
paul_nicholls
Registered User
 
Join Date: Sep 2011
Location: Australia, Tasmania
Posts: 180
or is it to make the ROM format header exactly 32 bytes?
paul_nicholls is offline   Reply With Quote
Old December 13th, 2011, 07:59   #98
tronix286
Registered User
 
tronix286's Avatar
 
Join Date: Aug 2010
Location: Russia, Moscow
Posts: 48
I like idea with ROM-header but i don't understand why header has so big size? My header version is:

Code:
0000h: 'CH16' = magic number
0004h: Reserved (allways 0)
0005h: Spec version (1-byte. The first 4-bits are major version and last 4-bits - minor version. So, we can code version from v0.0 to v16.16')
0006h: rom length (excl. header) For future spec, size might be over 64kb.
000Ah: start address (default to 0) Max: 0xFFFF
000Ch: CRC-32 checksum; polynom 0x04C11DB7 (excl. header)
0010h: start of binary code
So, header size is 16-bytes only.

Last edited by tronix286; December 13th, 2011 at 08:15..
tronix286 is offline   Reply With Quote
Old December 13th, 2011, 08:44   #99
paul_nicholls
Registered User
 
Join Date: Sep 2011
Location: Australia, Tasmania
Posts: 180
Quote:
Originally Posted by tronix286 View Post
I like idea with ROM-header but i don't understand why header has so big size? My header version is:

Code:
0000h: 'CH16' = magic number
0004h: Reserved (allways 0)
0005h: Spec version (1-byte. The first 4-bits are major version and last 4-bits - minor version. So, we can code version from v0.0 to v16.16')
0006h: rom length (excl. header) For future spec, size might be over 64kb.
000Ah: start address (default to 0) Max: 0xFFFF
000Ch: CRC-32 checksum; polynom 0x04C11DB7 (excl. header)
0010h: start of binary code
So, header size is 16-bytes only.
Nice! +1
paul_nicholls is offline   Reply With Quote
Old December 13th, 2011, 10:40   #100
refraction
PCSX2 Coder
 
refraction's Avatar
 
Join Date: Jan 2004
Location: Plymouth, UK
Posts: 10,037
quick question at the fear of looking like an idiot,

does this mean we will only load in to memory the data from the start of the binary? or should the default start address actually be 0020h or 0010h (depending on the 16bit or 32bit header)?
__________________

http://www.pcsx2.net
Intel i7 920 @ 3.4Ghz, POV GTX 570 1.3Gb, 1.8Tb HD space, 6Gb OCZ Reaper PC3-14400 Triple Channel
Dont PM me for help, use the forums, thats what its for!


My Chip16 Emulator RefChip16 http://code.google.com/p/refchip16/
refraction is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

All times are GMT +1. The time now is 16:19.

© 2006 - 2012 Emu Forums | About Emu Forums | Advertisers | Investors | Legal | A member of the Crowdgather Forum Community


Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2013, vBulletin Solutions, Inc.