- Top Stats

Top Posters Top Topic Starters Top Topics
Devilmarku... 28
Leonie 14
TFM 13
Langweil 5
oobdoo 4
Devilmarku... 1
Langweil 1
CPCIak 1
Leonie 1
funkheld 1
JavaCPC Beta z... - 11924 Views Devilmarku... 22:15, 10 September 09
Multiface 2 RO... - 6013 Views CPCIak 13:29, 31 December 09
AY-Emulation - 5464 Views Leonie 17:11, 10 April 10
JavaCPC-Applet... - 2857 Views Langweil 21:27, 30 January 12
CPM : Disckit3... - 2602 Views funkheld 12:41, 16 October 10


Author Topic: AY-Emulation  (Read 5465 times)

0 Members and 1 Guest are viewing this topic.

Offline Leonie

  • CPC6128
  • ****
  • Posts: 232
  • Country: de
  • Liked: 0
AY-Emulation
« on: 17:11, 10 April 10 »
Mir ist aufgefallen, dass der CPC-Emulator WinApe den AY-Chip miserabel emuliert.
So mancher Tune fällt komplett in sich zusammen.
Total unverständlich, ist doch WinApe ansonsten ein echt üppiger Emu.
Der Emulator "CPCE" macht die Sache schon viel besser, auch wenn er die Hüllkurven teilweise zu extrem arbeiten lässt. (und leider nur in Mono)
 
 

Offline Devilmarkus

  • Vivid source of indefiniteness
  • 6128 Plus
  • ******
  • Posts: 4.039
  • Country: de
  • WebCPC / JavaCPC developer
    • index.php?action=treasury
    • CPC-Live website
  • Liked: 1007
Re: AY-Emulation
« Reply #1 on: 18:03, 10 April 10 »
Mir ist aufgefallen, dass der CPC-Emulator WinApe den AY-Chip miserabel emuliert.
So mancher Tune fällt komplett in sich zusammen.
Total unverständlich, ist doch WinApe ansonsten ein echt üppiger Emu.
Der Emulator "CPCE" macht die Sache schon viel besser, auch wenn er die Hüllkurven teilweise zu extrem arbeiten lässt. (und leider nur in Mono)

Der WinAPE hat nur lineare Lautstärke. Er unterscheidet zwischen 0 (kein Ton) und 15 (Lautester Ton).
CPCE ist da schon anders. (Sollte eigentlich schon Stereo sein!)
CPCE verwendet eine Routine die im CPC Basic in etwa so aussieht:
Code: [Select]
FOR n=0 TO 15: PRINT 15*(2^((1+n)/2))/256: NEXT nDies ist zwar schon besser, allerdings klingt der Sound zu abrupt beendet.

JavaCPC verwendet einen Logarithmus, welcher dem AY Chip am meisten ähnelt:
Code: [Select]
        0, 0.117418174, 0.189517052, 0.283588922, 0.440146487,
        0.741130694, 1.12748913, 2.08514534, 2.36758984, 4.09155413,
        5.64934768, 6.96772717, 8.8908217, 10.8194095, 12.9095903, 15

Hier mal eine Veranschaulichung des Lautstärkeverlaufes:
WinAPE (Linear):
 
CPCE (Einfach kalkulierter Algorithmus):

JavaCPC/WebCPC (Berechnete Werte anhand von AY Dokumentation):


Wie Du also sehen kannst, sind in der Lautstärke deutliche Unterschiede im Verlauf zu sehen (Leise zu Laut)
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

Offline Devilmarkus

  • Vivid source of indefiniteness
  • 6128 Plus
  • ******
  • Posts: 4.039
  • Country: de
  • WebCPC / JavaCPC developer
    • index.php?action=treasury
    • CPC-Live website
  • Liked: 1007
Re: AY-Emulation
« Reply #2 on: 18:19, 10 April 10 »
Schönes Beispiel für die korrekte AY-Wiedergabe ist Musik.
Demo Iz Art mal hier reingestellt:

[cpc=http://cpcwiki.eu/useruploads/content/DemoIzArt.dsk,dia,1]CPC6128[/cpc]

Download

Kannst Du mal gerne vergleichen. (Ich finde, im JavaCPC klingt der Sound am sattesten und sehr hübsch wiedergegeben, ich weiss, Eigenlob stinkt, aber ich würde genauso auch negative Kritik hier schreiben)
Ich weiss allerdings nicht, ob die Demo auf dem CPCE läuft. Der erste Teil sollte aber.
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

Offline Leonie

  • CPC6128
  • ****
  • Posts: 232
  • Country: de
  • Liked: 0
Re: AY-Emulation
« Reply #3 on: 19:58, 10 April 10 »
Okay, das probiere ich jetzt gleich mal aus.
 
PS:
Unter WinApe klingen auch manche tiefe Rechteck-Wellenformen seltsam kratzend/grollend, als ob der Tune zu tief abgespielt würde.
Bei "CPCE" hast du geschrieben, dass der Sound zu abrupt endet.
Meinst du damit die schnell nachlassende Lautstärke nach dem sehr ausgeprägten Attack?
Ich finde, die Noten "hüpfen" dadurch bei manchen Tunes zu heftig, bzw. klingen zu kurz.
Das kann, neben dem Klangbild, unter Umständen auch den Groove verändern/stören, im Glücksfall evt. auch verbessern.

Offline Devilmarkus

  • Vivid source of indefiniteness
  • 6128 Plus
  • ******
  • Posts: 4.039
  • Country: de
  • WebCPC / JavaCPC developer
    • index.php?action=treasury
    • CPC-Live website
  • Liked: 1007
Re: AY-Emulation
« Reply #4 on: 19:59, 10 April 10 »
Bei "CPCE" hast du geschrieben, dass der Sound zu abrupt endet.
Meinst du damit die schnell nachlassende Lautstärke nach dem sehr ausgeprägten Attack?
Ich finde, die Noten "hüpfen" dadurch bei manchen Tunes zu heftig, bzw. klingen zu kurz.

Ja, genau das meine ich.
Die Lautstärkekurve fällt im CPCE zu schnell ab.
Das klingt dann, als ob die Töne komprimiert sind.

Wohingegen der WinApe laut drauflos böllert... (Dynamik = 0)
Dort hört man also während des Attack's und langsam sinkender Lautstärke immernoch viel zu lauten Ton, der eigentlich im original CPC schon längst verstummt sein müsste/ um einiges leiser sein müsste.

Ausserdem:
Der WinAPE spielt Geräusche (Noise) zu "hell" ab (ziemlich genau 1 Oktave zu hoch)
Beste Beispiel hier: Hexenküche 1 / Cauldron 1
Ich nehm mal was auf und stell es hier gleich rein...
« Last Edit: 20:05, 10 April 10 by Devilmarkus »
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

Offline Devilmarkus

  • Vivid source of indefiniteness
  • 6128 Plus
  • ******
  • Posts: 4.039
  • Country: de
  • WebCPC / JavaCPC developer
    • index.php?action=treasury
    • CPC-Live website
  • Liked: 1007
Re: AY-Emulation
« Reply #5 on: 20:23, 10 April 10 »
Hier der Testvergleich:
Cauldron 1 (JavaCPC / WinAPE)

Sicherlich ist auch JavaCPC's AY Emulation durchaus nicht perfekt, aber in meinen Ohren klingt das deutlich besser.
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

Offline Leonie

  • CPC6128
  • ****
  • Posts: 232
  • Country: de
  • Liked: 0
Re: AY-Emulation
« Reply #6 on: 21:08, 10 April 10 »
Das White-Noise ist bei WinApe in der Tat viel höher gestimmt.
Habe mir eben das Titelstück von "Beyond the ice palace" (David Whittaker) angehört, dort ist es ebenfalls deutlich zu hören, 1 Oktave Unterschied könnte gut hinkommen.
 
 

Offline Leonie

  • CPC6128
  • ****
  • Posts: 232
  • Country: de
  • Liked: 0
Re: AY-Emulation
« Reply #7 on: 21:13, 10 April 10 »
Könntest du noch ein bis mehrere andere Klangbeispiele (JavaCPC VS WinApe) beisteuern?
Würde mich sehr interessieren.
 

Offline Devilmarkus

  • Vivid source of indefiniteness
  • 6128 Plus
  • ******
  • Posts: 4.039
  • Country: de
  • WebCPC / JavaCPC developer
    • index.php?action=treasury
    • CPC-Live website
  • Liked: 1007
Re: AY-Emulation
« Reply #8 on: 21:18, 10 April 10 »
Funktioniert JavaCPC denn bei Dir nicht?
Ich hab gerade eine aktuelle Beta kompiliert...
Downloadlink gibt es allerdings nicht offiziell...
Schicke ich Dir als PM. ;)
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

Offline Executioner

  • Supporter
  • 6128 Plus
  • *
  • Posts: 783
  • Country: au
  • WinAPE Developer
    • WinAPE
  • Liked: 390
Re: AY-Emulation
« Reply #9 on: 05:07, 11 April 10 »
Unfortunately, I can't speak German. If there are known problems with WinAPE AY emulation, I'd like to know. WinAPE is not linear at all, try measuring the output!

Offline Devilmarkus

  • Vivid source of indefiniteness
  • 6128 Plus
  • ******
  • Posts: 4.039
  • Country: de
  • WebCPC / JavaCPC developer
    • index.php?action=treasury
    • CPC-Live website
  • Liked: 1007
Re: AY-Emulation
« Reply #10 on: 11:15, 11 April 10 »
Unfortunately, I can't speak German. If there are known problems with WinAPE AY emulation, I'd like to know. WinAPE is not linear at all, try measuring the output!
We were talking about AY emulation.
It sounds like WinAPE has linear sound output like JEMU. (Maybe I am wrong here)
Also the NOISE is about 1 octave too high. (Check the WAV-files from attached zip file here)
Leonie also means, that the attacks in WinAPE's AY are too long/loud.
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

Offline Leonie

  • CPC6128
  • ****
  • Posts: 232
  • Country: de
  • Liked: 0
Re: AY-Emulation
« Reply #11 on: 14:25, 11 April 10 »
Hi Devilmarkus,
ich habe JavaCPC installiert und es funktioniert! Hurra!  :laugh:
Der Emulator macht einen sehr edlen Eindruck, er hat gute Chancen, mein Lieblingsemulator zu werden.
Aber wieso ist die Samplerate ab Werk auf 22 Khz eingestellt?
Da gehen einige Obertöne flöten, 44 Khz sollten es schon sein.
Interessant sind die verschiedenen Sound-"Kennlinien", die man einstellen kann.
Werden die 8 AY-Hüllkurven ebenfalls naturgetreu abgebildet?
Nicht jeder Tune benutzt diese, aber wie verhalten sich die verschiedenen Emulatoren in dieser Hinsicht? Weißt du das? Beeinflussen die 16 Lautstärkestufen, je nachdem wie sie emuliert werden, auch das Klangverhalten der Hüllkurven?
Und noch ne Frage: Kennst du den MultiMachineEmulator? Dieser erzeugt ebenfalls einen extrem Attack-armen Klang, noch extremer als WinApe, meine ich.
 

Offline Devilmarkus

  • Vivid source of indefiniteness
  • 6128 Plus
  • ******
  • Posts: 4.039
  • Country: de
  • WebCPC / JavaCPC developer
    • index.php?action=treasury
    • CPC-Live website
  • Liked: 1007
Re: AY-Emulation
« Reply #12 on: 14:34, 11 April 10 »
Hi Leonie,
Die Samplerate habe ich deswegen auf 22khz, weil 44/48 khz teilweise Probleme bereiten bei der Wiedergabe (knacken).

Du kannst sie aber höher einstellen. Die Änderung wird nach einem Emu-Neustart wirksam. (Irgendwie muss ich noch eine Meldung diesbezüglich in den Dialog pressen)
Freut mich, wenn er bei Dir funktioniert.
Er ist noch nicht fertig drum der "private" Downloadlink.

JavaCPC hat übrigens keine 100%ige eigene Emulation!
Er basiert auf JEMU http://jemu.winape.net
Ich habe nur den Core verbessert, einige Funktionen dazu programmiert und einige Sachen (CRTC, AY, FDC) erheblich verbessert.

Zu den Hüllkurven kann ich Dir nur soviel sagen: Die werden doch Softwareseitig erzeugt, soweit ich weiss... Also sollten sie auch funktionieren.

Multimachine Emulator kenne ich noch aus Windoof'98-Zeiten.
Funktioniert bei mir nicht richtig.

Etwas zu emulieren kann leider nie 100% der Realität entsprechen. Gerade solche Sachen, wie einen kompletten Chip zu simulieren sind nicht einfach.
Der Richard Wilson (Executioner) hat da echt gute Arbeit geleistet. (Auch, wenn JEMU wirklich spartanisch ist, emuliert er relativ gut, taugt allerdings als "Standalone-Anwendung" nichts.)
Wie geschrieben basiert JavaCPC darauf und verwendet folglich auch die (zwar abgeänderten) Routinen aus JEMU.
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

Offline Leonie

  • CPC6128
  • ****
  • Posts: 232
  • Country: de
  • Liked: 0
Re: AY-Emulation
« Reply #13 on: 15:04, 11 April 10 »
Zu den Hüllkurven kann ich Dir nur soviel sagen: Die werden doch Softwareseitig erzeugt, soweit ich weiss... Also sollten sie auch funktionieren.

Meinst du softwareseitig auf dem echten CPC/AY?
Ich bin mir fast sicher, dass es sich um Hardware-Hüllkurven handelt.
Wenn nicht, wer berechnet diese Hüllkurven? Der AY oder Z80?
Sollten die Hüllkurven softwaremäßig arbeiten, wieso war es dann nicht möglich, diese Hüllkurven zu verändern? Die 8 möglichen Hüllkurven waren ja schon in Stein gemeißelt.
Wie gesagt, ich bin mir auch nicht ganz sicher.
 

Offline Devilmarkus

  • Vivid source of indefiniteness
  • 6128 Plus
  • ******
  • Posts: 4.039
  • Country: de
  • WebCPC / JavaCPC developer
    • index.php?action=treasury
    • CPC-Live website
  • Liked: 1007
Re: AY-Emulation
« Reply #14 on: 15:09, 11 April 10 »
Nun, ich bin mir da auch nicht sicher.
Wie gesagt kenne ich mich mit Emulation so gut wie garnicht aus.
Hier kannst Du den Source sehen zu JavaCPC's AY emulation:
Code: [Select]
package jemu.core.device.sound;

import jemu.core.device.*;
import jemu.ui.Switches;
import jemu.ui.Display;
import jemu.ui.Desktop;

/**
 * Title:        JEMU
 * Description:  The Java Emulation Platform
 * Copyright:    Copyright (c) 2002
 * Company:
 * @author
 * @version 1.0
 */
public class AY_3_8910 extends SoundDevice {

    int mfpPrediv[] = {0, 4, 10, 16, 50, 64, 100, 200};
    public static final double[] LINEAR_VOLUME = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
    };
    protected int[] Buffered = new int[1000];
    //
    // AY Amplitude values taken from CPCe95
    // and re-calculated for JavaCPC
    //
    // To calculate:
    // FOR n=0 TO 15: PRINT 15*(2^((1+n)/2))/256: NEXT n
    public static final double[] LOG_VOLUME = {
        0.08286408, 0.1171875, 0.1657282, 0.234375,
        0.3314563, 0.46875, 0.6629126, 0.9375,
        1.325825, 1.875, 2.65165, 3.75,
        5.303301, 7.5, 10.6066, 15
    };

    //
    // AY Amplitude values taken from Ay_Emul
    // and re-calculated for JavaCPC (divided with 4369)
    //

    // { (c)Hacker KAY }
    //  Amplitudes_AY:array[0..15]of Word=
    //   (0, 836, 1212, 1773, 2619, 3875, 5397, 8823, 10392, 16706, 23339,
    //   29292, 36969, 46421, 55195, 65535);
    public static final double[] LOG_VOLUME_A = {
        0, 0.117418174, 0.189517052, 0.283588922, 0.440146487,
        0.741130694, 1.12748913, 2.08514534, 2.36758984, 4.09155413,
        5.64934768, 6.96772717, 8.8908217, 10.8194095, 12.9095903, 15
    };

    //{ (c)V_Soft }
    // Amplitudes_AY:array[0..15]of Word=
    //   (0, 513, 828, 1239, 1923, 3238, 4926, 9110, 10344, 17876, 24682,
    //   30442, 38844, 47270, 56402, 65535);}
    public static final double[] LOG_VOLUME_B = {
        0, 0.191348135, 0.277409018, 0.405813687, 0.599450675,
        0.886930648, 1.23529412, 2.01945525, 2.37857633, 3.8237583,
        5.34195468, 6.70450904, 8.46166171, 10.6250858, 12.6333257, 15
    };
    protected int vuL,  vuR;
    protected int leftchange,  rightchange,  midchange;
    public boolean register13Updated = false;
    public static boolean digiblast = false;
    public static int leftChannel,  rightChannel,  blasterA,  blasterB;
    protected int volumen = 0;
    int volcount, vucount;
    protected int digibuffer = 20000;
    public static int digicount = 0;
    public final int BDIR_MASK = 0x04;
    public final int BC2_MASK = 0x02;
    public final int BC1_MASK = 0x01;
    public final int PORT_A = 0;
    public final int PORT_B = 1;

    // Possible states
    protected static final int INACTIVE = 0;
    protected static final int LATCH = 1;
    protected static final int READ = 2;
    protected static final int WRITE = 3;
    protected static final int[] STATES = {
        INACTIVE, LATCH, INACTIVE, READ, LATCH, INACTIVE, WRITE, LATCH
    };
    // Registers
    protected static final int AFINE = 0;
    protected static final int ACOARSE = 1;
    protected static final int BFINE = 2;
    protected static final int BCOARSE = 3;
    protected static final int CFINE = 4;
    protected static final int CCOARSE = 5;
    protected static final int NOISEPERIOD = 6;
    protected static final int ENABLE = 7;
    protected static final int AVOL = 8;
    protected static final int BVOL = 9;
    protected static final int CVOL = 10;
    protected static final int EFINE = 11;
    protected static final int ECOARSE = 12;
    protected static final int ESHAPE = 13;
    protected static final int REG_PORTA = 14;
    protected static final int REG_PORTB = 15;
    // Bits of ENABLE register
    protected static final int ENABLE_A = 0x01;
    protected static final int ENABLE_B = 0x02;
    protected static final int ENABLE_C = 0x04;
    protected static final int NOISE_A = 0x08;
    protected static final int NOISE_B = 0x10;
    protected static final int NOISE_C = 0x20;
    protected static final int PORT_A_OUT = 0x40;
    protected static final int PORT_B_OUT = 0x80;
    protected static final int NOISE_ALL = NOISE_A | NOISE_B | NOISE_C;
    // Sound Channels (inc Noise and Envelope)
    protected static final int A = 0;
    protected static final int B = 1;
    protected static final int C = 2;
    protected static final int NOISE = 3;
    protected static final int ENVELOPE = 4;
    protected int step = 0x8000;
    protected static int[] regs = new int[16];
    protected int selReg = 0;
    protected int bdirBC2BC1 = 0;
    protected int state = INACTIVE;
    protected int clockSpeed = 1000000;
    protected IOPort[] ports = new IOPort[]{
        new IOPort(IOPort.READ), new IOPort(IOPort.READ)
    };
    protected int[] envelope = new int[3];  // Channels A, B and C
    protected int[] output = new int[4];  // A, B, C and Noise
    protected int[] count = new int[5];  // A, B, C, Noise and Envelope counters
    protected int[] period = new int[5];  // A, B, C, Noise and Envelope
    protected int[] volume = new int[5];  // A, B, C, Noise and Envelope (Vol[3] not used)
    protected int outN,  random = 1;
    protected int countEnv,  hold,  alternate,  attack,  holding;
    protected int updateStep;

    public AY_3_8910() {
        super("AY-3-8910/2/3 Programmable Sound Generator");
        setClockSpeed(clockSpeed);
        player = SoundUtil.getSoundPlayer(200, true);
        player.setFormat(SoundUtil.UPCM8);
    }

    public void setClockSpeed(int value) {
        clockSpeed = value;
        updateStep = (int) (((long) step * 8L * (long) JavaSound.SAMPLE_RATE) / (long) clockSpeed);
//    updateStep = 5780; //(int)(((long)step * 8L * (long)audio.getSampleRate()) / (long)clockSpeed); //
        output[NOISE] = 0xff;
        for (int i = A; i <= ENVELOPE; i++) {
            period[i] = count[i] = updateStep;
        }
        period[ENVELOPE] = 0;
        count[NOISE] = 0x7fff;
    }

    public void changeClockSpeed(int value) {
        clockSpeed = value;
        updateStep = (int) (((long) step * 8L * (long) JavaSound.SAMPLE_RATE) / (long) clockSpeed);
    }

    public void setSelectedRegister(int value) {
        selReg = value & 0x0f;
    }

    public int getSelectedRegister() {
        return selReg;
    }

    public void setBDIR_BC2_BC1(int value, int dataValue) {
        if (bdirBC2BC1 != value) {
            bdirBC2BC1 = value;
            state = STATES[bdirBC2BC1];
            writePort(0, dataValue);
        }
    }

    public int getBDIR_BC2_BC1() {
        return bdirBC2BC1;
    }

    public int readPort(int port) {
        if (selReg != 0) {
            return state == READ ? readRegister(selReg) : 0xff;
        }
        return 0xff;
    }
//[6]>>5)&7]

    public void setReg(int value) {
        //  if (selReg == 4)value^=Switches.diagnose;
        setRegister(selReg, value);
    }

    public void writePort(int port, int value) {
//      if (port == 0xfbfe || port == 0xfbee)
//          return;
        switch (state) {
            case LATCH:
                selReg = value & 0x0f;
                break;
            case WRITE:
                setReg(value);
                break;
        }
    }

    public int getRegister(int index) {
        return regs[index];
    }

    public static String getReg(int index) {
        return jemu.core.Util.hex(regs[index]).substring(6);
    }

    public int readRegister(int index) {
        return index < REG_PORTA ? regs[index] : ports[index - REG_PORTA].read();
    }

    public boolean registerUpdated() {
        return register13Updated;
    }

    public void resetUpdated() {
        register13Updated = false;
    }

    public void setRegister(int index, int value) {
        if (index == 13) {
            register13Updated = true;
        }

        if (index < REG_PORTA) {
            if (index == ESHAPE || regs[index] != value) {
                regs[index] = value;
                switch (index) {
                    case ACOARSE:
                    case BCOARSE:
                    case CCOARSE:
                    case AFINE:
                    case BFINE:
                    case CFINE: {
                        index >>= 1;
                        int val = (((regs[(index << 1) + 1] & 0x0f) << 8) | regs[index << 1]) * updateStep;
                        int last = period[index];
                        period[index] = val = val < 0x8000 ? 0x8000 : val;
                        int newCount = count[index] - (val - last);
                        count[index] = newCount < 1 ? 1 : newCount;
                        break;
                    }

                    case NOISEPERIOD: {
                        int val = (value & 0x1f) * updateStep;
                        if (!Switches.ayeffect) {
                            val *= 2;
                        }
                        int last = period[NOISE];
                        period[NOISE] = val = val == 0 ? updateStep : val;
                        int newCount = count[NOISE] - (val - last);
                        count[NOISE] = newCount < 1 ? 1 : newCount;
                        break;
                    }

                    case ENABLE:
                        break;

                    case AVOL:
                    case BVOL:
                    case CVOL: {
                        volume[index - AVOL] = (value & 0x10) == 0 ? value & 0x0f : volume[ENVELOPE];
                        break;
                    }

                    case EFINE:
                    case ECOARSE: {
                        int val = (((regs[ECOARSE] << 8) | regs[EFINE]) * updateStep) << 1;
                        int last = period[ENVELOPE];
                        period[ENVELOPE] = val;
                        int newCount = count[ENVELOPE] - (val - last);
                        count[ENVELOPE] = newCount < 1 ? 1 : newCount;
                        break;
                    }

                    case ESHAPE: {
                        attack = (value & 0x04) == 0 ? 0 : 0x0f;
                        if ((value & 0x08) == 0) {
                            hold = 1;
                            alternate = attack;
                        } else {
                            hold = value & 0x01;
                            alternate = value & 0x02;
                        }
                        count[ENVELOPE] = period[ENVELOPE];
                        countEnv = 0x0f;
                        holding = 0;
                        int vol = volume[ENVELOPE] = attack ^ 0x0f;
                        if ((regs[AVOL] & 0x10) != 0) {
                            volume[A] = vol;
                        }
                        if ((regs[BVOL] & 0x10) != 0) {
                            volume[B] = vol;
                        }
                        if ((regs[CVOL] & 0x10) != 0) {
                            volume[C] = vol;
                        }
                        break;
                    }
                }
            }
        } else {
            ports[index - REG_PORTA].write(value);
        }
    }

    public void writeAudio() {
        int enable = regs[ENABLE];
        if ((enable & ENABLE_A) != 0) {
            if (count[A] <= step) {
                count[A] += step;
            }
            output[A] = 1;
        }
        if ((enable & ENABLE_B) != 0) {
            if (count[B] <= step) {
                count[B] += step;
            }
            output[B] = 1;
        }
        if ((enable & ENABLE_C) != 0) {
            if (count[C] <= step) {
                count[C] += step;
            }
            output[C] = 1;
        }
        outN = output[NOISE] | enable;
        if ((enable & NOISE_ALL) == NOISE_ALL) { // false if All disabled
            if (count[NOISE] <= step) {
                count[NOISE] += step;
            }
        }
        // laceimage Sound bytes
        int[] cnt = new int[3];
        int left = step;
        do {
            int add = count[NOISE] < left ? count[NOISE] : left;

            for (int chan = A; chan <= C; chan++) {
                int chcnt = count[chan];
                if ((outN & (NOISE_A << chan)) != 0) {
                    int val = output[chan] == 0 ? cnt[chan] : cnt[chan] + chcnt;
                    if ((chcnt -= add) <= 0) {
                        int p = period[chan];
                        while (true) {
                            if ((chcnt += p) > 0) {
                                if ((output[chan] ^= 0x01) != 0) {
                                    val += p - chcnt;
                                }
                                break;
                            }
                            val += p;
                            if ((chcnt += p) > 0) {
                                if (output[chan] == 0) {
                                    val -= chcnt;
                                }
                                break;
                            }
                        }
                    } else if (output[chan] != 0) {
                        val -= chcnt;
                    }
                    cnt[chan] = val;
                } else {
                    if ((chcnt -= add) <= 0) {
                        int p = period[chan];
                        while (true) {
                            if ((chcnt += p) > 0) {
                                output[chan] ^= 0x01;
                                break;
                            }
                            if ((chcnt += p) > 0) {
                                break;
                            }
                        }
                    }
                }
                count[chan] = chcnt;
            }

            if ((count[NOISE] -= add) <= 0) {
                int val = random + 1;
                if ((val & 0x02) != 0) {
                    outN = (output[NOISE] ^= 0xff) | enable;
                }
                random = (random & 0x01) == 0 ? random >> 1 : (random ^ 0x28000) >> 1;
                count[NOISE] += period[NOISE];
            }

            left -= add;
        } while (left > 0);

        if (holding == 0 && period[ENVELOPE] != 0) {
            if ((count[ENVELOPE] -= step) <= 0) {
                int ce = countEnv;
                int p = period[ENVELOPE];
                do {
                    ce--;
                } while ((count[ENVELOPE] += p) <= 0);

                if (ce < 0) {
                    if (hold != 0) {
                        if (alternate != 0) {
                            attack ^= 0x0f;
                        }
                        holding = 1;
                        ce = 0;
                    } else {
                        if (alternate != 0 && (ce & 0x10) != 0) {
                            attack ^= 0x0f;
                        }
                        ce &= 0x0f;
                    }
                }
                countEnv = ce;
                int vol = volume[ENVELOPE] = ce ^ attack;
                if ((regs[AVOL] & 0x10) != 0) {
                    volume[A] = vol;
                }
                if ((regs[BVOL] & 0x10) != 0) {
                    volume[B] = vol;
                }
                if ((regs[CVOL] & 0x10) != 0) {
                    volume[C] = vol;
                }
            }
        }

        int a = (int) (LOG_VOLUME_A[volume[A]]) * cnt[A] >> 13;
        int b = (int) (LOG_VOLUME_A[volume[B]]) * cnt[B] >> 13;
        int c = (int) (LOG_VOLUME_A[volume[C]]) * cnt[C] >> 13;
        if (Switches.VSoftOutput) {
            a = (int) (LOG_VOLUME_B[volume[A]]) * cnt[A] >> 13;
            b = (int) (LOG_VOLUME_B[volume[B]]) * cnt[B] >> 13;
            c = (int) (LOG_VOLUME_B[volume[C]]) * cnt[C] >> 13;
        }
        if (Switches.CPCE95) {
            a = (int) (LOG_VOLUME[volume[A]]) * cnt[A] >> 13;
            b = (int) (LOG_VOLUME[volume[B]]) * cnt[B] >> 13;
            c = (int) (LOG_VOLUME[volume[C]]) * cnt[C] >> 13;
        }

        if (Switches.linear) {
            a = (int) (LINEAR_VOLUME[volume[A]]) * cnt[A] >> 13;
            b = (int) (LINEAR_VOLUME[volume[B]]) * cnt[B] >> 13;
            c = (int) (LINEAR_VOLUME[volume[C]]) * cnt[C] >> 13;

        }
        a *= Switches.volume;
        b *= Switches.volume;
        c *= Switches.volume;
        if (Switches.ayeffect) {
            b = (int) ((long) b * 0.75);
        }
        if (digiblast) {
            BvuMeter();
        } else {
            vuMeter(a, b, c);
        }
        leftChannel = (a + b);
        rightChannel = (b + c);

        soundOutput(leftChannel, rightChannel);
    }

    public void setReadDevice(int port, Device device, int readPort) {
        ports[port].setInputDevice(device, readPort);
    }

    public void soundOutput(int left, int right) {
        if (digiblast) {
            leftChannel = blasterA;

            rightChannel = blasterB;
            if (Switches.audioenabler != 1) {
                leftChannel = blasterA = rightChannel = blasterB = 0;
            }
        } else {
            if (Switches.audioenabler != 1) {
                leftChannel = rightChannel = 0;
            }
        }

        if (digicount != 0) {
            digicount++;
        }
        if (digicount >= digibuffer) {
            digiblast = false;
            digicount = 0;
        }


        if (updatecount != 0) {
            updatecount++;
            if (updatecount >= 4000) {
                updateBuffer();
            }
        }
        if (Switches.dbeffect && updatecount == 0) {
            buffer_write(rightChannel);
            rightChannel = buffer_read();
        }
        player.writeStereo(leftChannel, rightChannel);
    }
    public static int buffersize = 1600;
    public static int updatecount = 0;

    protected void updateBuffer() {
        try {
            if (buffersize != circlebuffer.length) {
                writepos = 0;
                circlebuffer = null;
                System.gc();
                writepos = buffersize - 1;
                circlebuffer = new int[buffersize];
                updatecount = 1;
            } else {
                updatecount = 0;
            }
        } catch (Exception e) {
            updatecount = 1;
        }
    }
    /* the buffer */
    int[] circlebuffer = new int[1600];

    /* write position  */
    int writepos = (1600 - 1);

    /* read position  */
    int readpos = 0;

    /* write value into buffer and update write position */
    void buffer_write(int val) {
        try {
            circlebuffer[writepos] = val;
            writepos = (writepos + 1) % circlebuffer.length;
        } catch (Exception e) {
            updatecount = 1;
        }
    }

    /* read value from buffer and update read position */
    int buffer_read() {
        try {
            int val = circlebuffer[readpos];
            readpos = (readpos + 1) % circlebuffer.length;
            return val;
        } catch (Exception e) {
            updatecount = 1;
            return 0;
        }
    }

    public void setWriteDevice(int port, Device device, int writePort) {
        ports[port].setOutputDevice(device, writePort);
    }

    public void vuMeter(int left, int mid, int right) {
        volcount++;
        if (volcount >= 0x400) {
            volcount = 0;
            if (left > leftchange) {
                leftchange = left;
            } else if (leftchange > 0) {
                leftchange--;
            }
            if (mid > midchange) {
                midchange = mid;
            } else if (midchange > 0) {
                midchange--;
            }
            if (right > rightchange) {
                rightchange = right;
            } else if (rightchange > 0) {
                rightchange--;
            }
            try {
                if (Desktop.midvumeter.isShowing()) {
                    Desktop.midvumeter.setValue(midchange);
                    Desktop.leftvumeter.setValue(leftchange);
                    Desktop.rightvumeter.setValue(rightchange);
                }
            } catch (Exception e) {
            }
            Display.left = leftchange;
            Display.right = rightchange;
        }
    }

    public void BvuMeter() {
        volcount++;
        if (volcount >= 0x300) {
            volcount = 0;
            int left = blasterA;
            if (left > leftchange) {
                leftchange = left;
            } else if (leftchange > 0) {
                leftchange--;
            }
            int right = blasterB;
            if (right > rightchange) {
                rightchange = right;
            } else if (rightchange > 0) {
                rightchange--;
            }
            try {
                if (Desktop.midvumeter.isShowing()) {
                    Desktop.leftvumeter.setValue((leftchange ^ 0x80) / 2);
                    Desktop.rightvumeter.setValue((rightchange ^ 0x80) / 2);
                    Desktop.midvumeter.setValue(0);
                }
            } catch (Exception e) {
            }

        }
    }
}

Es sind einige Sachen "mehr" drin als reingehören, weil ich den AY dazu missbrauche, auch den Digiblaster wiederzugeben.
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

Offline Devilmarkus

  • Vivid source of indefiniteness
  • 6128 Plus
  • ******
  • Posts: 4.039
  • Country: de
  • WebCPC / JavaCPC developer
    • index.php?action=treasury
    • CPC-Live website
  • Liked: 1007
Re: AY-Emulation
« Reply #15 on: 19:12, 12 April 10 »
Übrigens habe ich noch etwas kurioses herausgefunden:
Das Spiel "Thing on a spring" klingt in anderen Emulatoren wie CPCE klasse!
WinAPE und JEMU/JavaCPC können in dem Spiel die Musik nicht richtig wiedergeben.
Stottert!

(In English)
I found out that the game "Thing on a spring" has well sound in emulators like CPCE but
fails in WinAPE and JEMU/JavaCPC. They can't playback the music properly. It's stocking!
@ Richard: Any idea what causes this? Perhaps you could figure this out and fix?
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

Offline Leonie

  • CPC6128
  • ****
  • Posts: 232
  • Country: de
  • Liked: 0
Re: AY-Emulation
« Reply #16 on: 22:16, 12 April 10 »
Übrigens habe ich noch etwas kurioses herausgefunden:
Das Spiel "Thing on a spring" klingt in anderen Emulatoren wie CPCE klasse!
WinAPE und JEMU/JavaCPC können in dem Spiel die Musik nicht richtig wiedergeben.
Stottert!

Also bei CPCE habe ich noch kein Spiel erlebt, welches den Sound nicht abspielen kann.
Von den überzeichneten Attacks mal abgesehen (die aber manchmal auch knackig klingen können, nicht selten stören sie aber auch, und bringen Groove/Klangbild durcheinander)
Bei WinAPE sieht es schon anders aus. Hier habe ich schon einige Spiele erlebt, bei denen der Sound stottert oder total entstellt klingt.
Ich suche bei Gelegenheit mal ein paar Spiele raus, bei denen WinAPE komplett versagt.
 
 

Offline Leonie

  • CPC6128
  • ****
  • Posts: 232
  • Country: de
  • Liked: 0
Re: AY-Emulation
« Reply #17 on: 18:00, 16 June 10 »
Hallo Markus!
 
Die "CPCe95"-AY-Lautstärkekurve in JavaCPC klingt nicht authentisch nach CPCe95.
Der Lautstärkeabfall ist zu heftig, hab das jetzt mehrmals überprüft.
Oder bin ich wahnsinnig?
 
Liebe Grüße  :)
 

Offline Devilmarkus

  • Vivid source of indefiniteness
  • 6128 Plus
  • ******
  • Posts: 4.039
  • Country: de
  • WebCPC / JavaCPC developer
    • index.php?action=treasury
    • CPC-Live website
  • Liked: 1007
Re: AY-Emulation
« Reply #18 on: 18:09, 16 June 10 »
Die "CPCe95"-AY-Lautstärkekurve in JavaCPC klingt nicht authentisch nach CPCe95.

Eine AY-emulation wird eh niemals wie eine Andere klingen.
Aber der Algorithmus ist Derselbe wie im CPCe95.
(Also die Lautstärkenkurve)

Quote
Oder bin ich wahnsinnig?
...
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

Offline Leonie

  • CPC6128
  • ****
  • Posts: 232
  • Country: de
  • Liked: 0
Re: AY-Emulation
« Reply #19 on: 18:23, 16 June 10 »
Aber der Algorithmus ist Derselbe wie im CPCe95.

Also bin ich wahnsinnig!
Sag´s doch gleich unverblümt!
Ja ich weiß, du wolltest mich nicht verletzen. Eigentlich nett von dir.  :'(
Aber ich war mir doch sicher, dass...
Nein, Schluss jetzt, nur der/die Wahnsinnige ist sich wirklich sicher!
Dennoch: Ich nehme einen Unterschied wahr.
 

Offline Leonie

  • CPC6128
  • ****
  • Posts: 232
  • Country: de
  • Liked: 0
Re: AY-Emulation
« Reply #20 on: 15:41, 24 June 10 »
Eine AY-emulation wird eh niemals wie eine Andere klingen.
Aber der Algorithmus ist Derselbe wie im CPCe95. (Also die Lautstärkenkurve)

Mir fehlt eine Lautstärkekurve, die einen Kompromiss zwischen CPCe95 und Hacker Kay darstellt.
Kann es sein, dass der CPCe95-Algorithmus auf den ersten vier Lautstärkestufen überhaupt kein Signal ausgibt?
Mir kommt es bei einigen Tunes so vor, als hätten diverse Instrumente einen viel zu kurzen Atem.