hallo , kann man mal in den sourcecoce reinschauen von dem exe- oder java-code von:
cdt2wav.
ich kann den ursprung-source nicht finden.
ich möchte gerne mal wissen wie dort die bytes als wav erzeugt werden.
danke.
gruss
JavaCPC benötigt dazu 3 Klassen:
package jemu.core.device.tape;
import jemu.core.Util;
/**
* Title: JavaCPC
* Description: The Java Amstrad CPC Emulator
* Copyright: Copyright (c) 2006-2010
* Company:
* @author
* @version 6.8
*/
/**
* CDT2WAV conversion utilities
* Converted from "TZX to WAV Converter v0.2 (C) 2006 Francisco Javier Crespo <tzx2wav@ya.com>"
*
* @author John Girvin
*
*/
public class CDT2WAV {
//
// CONSTANTS
//
// Header magic number bytes of ZXTAPE format file
private static byte[] ZXTAPE_HEADER = new byte[]{0x5A, 0x58, 0x54, 0x61, 0x70, 0x65, 0x21};
//
// INSTANCE VARIABLES
//
protected boolean debug = false;
private byte[] inpbuf;
private int frequency = 44100;
private CDT2WAVBaseOutput output;
private int currentBlock; // Current block that is playing
private int numBlocks; // Total Num. of blocks
private int blockStart[]; // Array of block start positions
public String ids[]; // Array for all IDs
public int blocks[]; // Array for all IDs
private int id; // Current Block ID
private int data; // Data to be played
private int datalen; // Len of ^^^
private int datapos; // Position in ^^^
private int bitcount; // How many bits to play in current byte ?
private int sb_bit; // should we play bit 0 or 1 ?
private byte databyte; // Current Byte to be replayed of the data
private int pilot; // Len of Pilot signal (in hp's)
private int sb_pilot; // Pilot pulse
private int sb_sync1; // Sync first half-period (hp)
private int sb_sync2; // Sync second
private int sb_bit0; // Bit-0
private int sb_bit1; // Bit-1
private int sb_pulse; // Pulse in Sequence of pulses and direct recording block
private int lastbyte; // How many bits are in last byte of data ?
private int pause; // Pause after current block (in milliseconds)
private int singlepulse; // Flag to activate single pulse waves
private short jump; // Relative Jump
private int loop_start = 0; // Position of the last Loop Start block
private int loop_count = 0; // Counter of the Loop
private int call_pos = 0; // Position of the last Call Sequence block
private int call_num = 0; // Number of Calls in the last Call Sequence block
private int call_cur = 0; // Current Call to be made
/*
private int cpc=0; // Amstrad CPC tape ?
*/
/**
* Create an object to convert ZXTAPE/CDT data to WAV format.
*
* @param input - the input data
* @param output - buffer to hold the WAV data. Supply null for test mode.
* @param freq - desired frequency in Hz for the WAV data
*/
public CDT2WAV(byte[] input, int freq) {
super();
inpbuf = input;
frequency = freq;
}
/**
* Dereference any allocated resources. Do not call this object again
* after calling dispose().
*/
public void dispose() {
inpbuf = null;
blockStart = null;
if (output != null) {
output.dispose();
}
output = null;
}
//
// TZX Blocks Parsing routines
//
// ...Standard Loading Data block
private void analyseID10() {
pause = get2(inpbuf, data);
datalen = get2(inpbuf, data + 2);
data += 4;
if (inpbuf[data] == 0x00) {
pilot = 8064;
} else {
pilot = 3220;
}
sb_pilot = output.samples(2168);
sb_sync1 = output.samples(667);
sb_sync2 = output.samples(735);
sb_bit0 = output.samples(885);
sb_bit1 = output.samples(1710);
lastbyte = 8;
}
// ...Custom Loading Data block
private void analyseID11() {
sb_pilot = output.samples(get2(inpbuf, data + 0));
sb_sync1 = output.samples(get2(inpbuf, data + 2));
sb_sync2 = output.samples(get2(inpbuf, data + 4));
sb_bit0 = output.samples(get2(inpbuf, data + 6));
sb_bit1 = output.samples(get2(inpbuf, data + 8));
pilot = get2(inpbuf, data + 10);
lastbyte = (int) inpbuf[data + 12];
pause = get2(inpbuf, data + 13);
datalen = get3(inpbuf, data + 15);
data += 18;
if (debug) {
System.out.println("Pilot is: " + pilot + " pause is: " + pause + " Length is: " + datalen);
}
}
// ...Pure Tone
private void analyseID12() {
sb_pilot = output.samples(get2(inpbuf, data + 0));
pilot = get2(inpbuf, data + 2);
while (pilot > 0) {
output.play(sb_pilot);
output.toggleAmp();
pilot--;
}
}
// ...Sequence of Pulses
private void analyseID13() {
pilot = (int) inpbuf[data + 0];
data++;
while (pilot > 0) {
sb_pulse = output.samples(get2(inpbuf, data + 0));
output.play(sb_pulse);
output.toggleAmp();
pilot--;
data += 2;
}
}
// ...Pure Data
private void analyseID14() {
sb_pilot = pilot = sb_sync1 = sb_sync2 = 0;
sb_bit0 = output.samples(get2(inpbuf, data + 0));
sb_bit1 = output.samples(get2(inpbuf, data + 2));
lastbyte = (int) inpbuf[data + 4];
pause = get2(inpbuf, data + 5);
datalen = get3(inpbuf, data + 7);
data += 10;
}
// ...Direct Recording
private void analyseID15() {
// For now the BEST way is to use the sample frequency for replay that is
// exactly the SAME as the Original Freq. used when sampling this block !
// i.e. NO downsampling is handled YET ... use TAPER when you need it ! ;-)
sb_pulse = output.samples(get2(inpbuf, data + 0));
if (sb_pulse == 0) {
sb_pulse = 1; // In case sample frequency > 44100
}
pause = get2(inpbuf, data + 2); // (Should work for frequencies up to 48000)
lastbyte = (int) inpbuf[data + 4];
datalen = get3(inpbuf, data + 5);
data = data + 8;
datapos = 0;
// Replay Direct Recording block ...
while (datalen > 0) {
if (datalen != 1) {
bitcount = 8;
} else {
bitcount = lastbyte;
}
databyte = inpbuf[data + datapos];
while (bitcount > 0) {
output.setAmp((databyte & 0x80) != 0);
output.play(sb_pulse);
databyte <<= 1;
bitcount--;
}
datalen--;
datapos++;
}
output.toggleAmp(); // Changed on 26-01-2005
if (pause != 0) {
output.pause(pause);
}
}
// ...Pause or Stop the Tape
private void analyseID20() {
pause = get2(inpbuf, data + 0);
output.setAmpNo();
if (debug) {
System.out.println("Pause is " + pause);
}
if (pause != 0) {
output.pause(pause);
} else {
output.pause(5000); // 5 seconds of pause in "Stop Tape" wave output
System.out.println("Pause is added: 5 secs");
}
output.setAmpLow();
}
// ...Group Start
private void analyseID21() {
// do nothing
}
// ...Group End
private void analyseID22() {
// do nothing
}
// ...Jump To Relative
private void analyseID23() {
jump = (short) (inpbuf[data + 0] + inpbuf[data + 1] * 256);
currentBlock += jump;
currentBlock--;
}
// ...Loop Start
private void analyseID24() {
loop_start = currentBlock;
loop_count = get2(inpbuf, data + 0);
}
// ...Loop End
private void analyseID25() {
loop_count--;
if (loop_count > 0) {
currentBlock = loop_start;
}
}
// ...Call Sequence
private void analyseID26() {
call_pos = currentBlock;
call_num = get2(inpbuf, data + 0);
call_cur = 0;
jump = (short) (inpbuf[data + 2] + inpbuf[data + 3] * 256);
currentBlock += jump;
currentBlock--;
}
// ...Return from Sequence
private void analyseID27() {
call_cur++;
if (call_cur == call_num) {
currentBlock = call_pos;
} else {
currentBlock = call_pos;
data = blockStart[currentBlock] + 1;
jump = (short) (inpbuf[data + call_cur * 2 + 2] + inpbuf[data + call_cur * 2 + 3] * 256);
currentBlock += jump;
currentBlock--;
}
}
// ...Stop the tape if in 48k mode
private void analyseID2A() {
output.pause(5000);
output.setAmpLow();
}
// ...Hardware Info
private void analyseID33() {
/*
if (inpbuf[data+1]==0 && inpbuf[data+2]>0x14 && inpbuf[data+2]<0x1a && inpbuf[data+3]==1) {
cpc=1;
}
*/
}
//
// UTILITY METHODS
//
// Conversion routines to fetch bytes in Big Endian order
private static int get2(byte[] data, int pos) {
return (data[pos] & 0xff | ((data[pos + 1] << 8) & 0xff00));
}
private static int get3(byte[] data, int pos) {
return (data[pos] & 0xff | (data[pos + 1] << 8) & 0xff00 | (data[pos + 2] << 16) & 0xff0000);
}
private static int get4(byte[] data, int pos) {
return (data[pos] & 0xff | (data[pos + 1] << 8) & 0xff00 | (data[pos + 2] << 16) & 0xff0000 | (data[pos + 3] << 24) & 0xff000000);
}
/**
* Count and optionally store the start positions of TZX blocks
* in the input[] data array
*
* @param blockstarts - array to be filled in with block start positions
* or null if this is not required.
*
* @return number of blocks found, or -1 if an unknown block was encountered
*/
private int countBlocks(int[] blockstarts) {
// Go through the file and record block starts
int pos = 10;
int numblocks = 0;
while (pos < inpbuf.length) {
if (blockstarts != null) {
blockstarts[numblocks] = pos;
}
pos++;
switch (inpbuf[pos - 1]) {
case 0x10:
pos += get2(inpbuf, pos + 0x02) + 0x04;
break;
case 0x11:
pos += get3(inpbuf, pos + 0x0F) + 0x12;
break;
case 0x12:
pos += 0x04;
break;
case 0x13:
pos += (inpbuf[pos + 0x00] * 0x02) + 0x01;
break;
case 0x14:
pos += get3(inpbuf, pos + 0x07) + 0x0A;
break;
case 0x15:
pos += get3(inpbuf, pos + 0x05) + 0x08;
break;
case 0x16:
pos += get4(inpbuf, pos + 0x00) + 0x04;
break;
case 0x17:
pos += get4(inpbuf, pos + 0x00) + 0x04;
break;
case 0x20:
pos += 0x02;
break;
case 0x21:
pos += inpbuf[pos + 0x00] + 0x01;
break;
case 0x22:
break;
case 0x23:
pos += 0x02;
break;
case 0x24:
pos += 0x02;
break;
case 0x25:
break;
case 0x26:
pos += get2(inpbuf, pos + 0x00) * 0x02 + 0x02;
break;
case 0x27:
break;
case 0x28:
pos += get2(inpbuf, pos + 0x00) + 0x02;
break;
case 0x2A:
pos += 0x04;
break;
case 0x30:
pos += inpbuf[pos + 0x00] + 0x01;
break;
case 0x31:
pos += inpbuf[pos + 0x01] + 0x02;
break;
case 0x32:
pos += get2(inpbuf, pos + 0x00) + 0x02;
break;
case 0x33:
pos += (inpbuf[pos + 0x00] * 0x03) + 0x01;
break;
case 0x34:
pos += 0x08;
break;
case 0x35:
pos += get4(inpbuf, pos + 0x10) + 0x14;
break;
case 0x40:
pos += get3(inpbuf, pos + 0x01) + 0x04;
break;
case 0x5A:
pos += 0x09;
break;
default:
return -1;
}
numblocks++;
}
return numblocks;
}
/**
* Run a pass of the conversion process, sending data to the supplied
* output manager object.
*
* @param output - the output manager
*/
private void convertPass(CDT2WAVBaseOutput output) {
// Initialise
currentBlock = 0;
singlepulse = 0;
// disable debug mode if this is a test run
if (output == null) {
debug = false;
}
// Start replay of blocks (Main loop of the program)
while (currentBlock < numBlocks) {
// Get ID of next block and start position in input byte array
id = inpbuf[blockStart[currentBlock]];
blocks[currentBlock] = output.outputTell();
ids[currentBlock] = getID(id);
if (debug) {
System.out.println("Block: " + getBlock(currentBlock) + " - ID: " + getID(currentBlock));
}
if (debug) {
System.out.println("ID is " + Util.hex(id));
}
data = blockStart[currentBlock] + 1;
switch (id) {
case 0x10:
analyseID10(); // Standard Loading Data block
break;
case 0x11:
analyseID11(); // Custom Loading Data block
break;
case 0x12:
analyseID12(); // Pure Tone
break;
case 0x13:
analyseID13(); // Sequence of Pulses
break;
case 0x14:
analyseID14(); // Pure Data
break;
case 0x15:
analyseID15(); // Direct Recording
break;
case 0x20:
analyseID20(); // Pause or Stop the Tape command
break;
case 0x21:
analyseID21(); // Group Start
break;
case 0x22:
analyseID22(); // Group End
break;
case 0x23:
analyseID23(); // Jump To Relative
break;
case 0x24:
analyseID24(); // Loop Start
break;
case 0x25:
analyseID25(); // Loop End
break;
case 0x26:
analyseID26(); // Call Sequence
break;
case 0x27:
analyseID27(); // Return from Sequence
break;
case 0x2A:
analyseID2A(); // Stop the tape if in 48k mode
break;
case 0x33:
analyseID33(); // Hardware Info
break;
// Ignored
case 0x30: // Description
case 0x31: // Message
case 0x32: // Archive Info
case 0x34: // Emulation info
case 0x35: // Custom Info
case 0x40: // Snapshot
case 0x5A: // ZXTape!
break;
// Unknown/Unsupported blocks
case 0x16: // C64 ROM Type Data Block
case 0x17: // C64 Turbo Tape Data Block
case 0x28: // Select Block
default: {
System.out.println("ERR_TZX_UNSUPPORTED");
break;
}
}
// TZX file blocks analysis finished
// Now we start generating the sound waves
if (id == 0x10 || id == 0x11 || id == 0x14) {
// One of the data blocks ...
// Play PILOT TONE
while (pilot > 0) {
output.play(sb_pilot);
output.toggleAmp();
pilot--;
}
// Play first SYNC pulse
if (sb_sync1 > 0) {
output.play(sb_sync1);
output.toggleAmp();
}
// Play second SYNC pulse
if (sb_sync2 > 0) {
output.play(sb_sync2);
output.toggleAmp();
}
// Play actual DATA
datapos = 0;
while (datalen > 0) {
if (datalen != 1) {
bitcount = 8;
} else {
bitcount = lastbyte;
}
databyte = inpbuf[data + datapos];
while (bitcount > 0) {
if ((databyte & 0x80) != 0) {
sb_bit = sb_bit1;
} else {
sb_bit = sb_bit0;
}
output.play(sb_bit); // Play first pulse of the bit
output.toggleAmp();
if (singlepulse == 0) {
output.play(sb_bit); // Play second pulse of the bit
output.toggleAmp();
}
databyte <<= 1;
bitcount--;
}
datalen--;
datapos++;
}
singlepulse = 0; // Reset flag for next TZX blocks
// If there is pause after block present then make first millisecond the oposite
// pulse of last pulse played and the rest in LOAMP ... otherwise don't do ANY pause
if (pause > 0) {
output.pause(1);
output.setAmpNo();
if (pause > 1) {
output.pause(pause - 1);
}
}
}
// We continue to replay the next TZX block
currentBlock++;
}
// 5 seconds of pause in "Stop Tape" wave output
output.pause(5000);
if (debug) {
System.out.println("End of tape... 5 seconds pause added");
}
output.stop();
if (debug) {
System.out.println(" OK");
}
}
/**
* MAIN CONVERSION ENTRY POINT
*
* @return array of data bytes generated, or null on error
*/
public byte[] convert() {
// Sanity checks
// ...check input and output
if (inpbuf == null || inpbuf.length < 10) {
System.out.println("ERR_ILLEGAL_ARGUMENT");
return null;
}
// ...check for TZX header
for (int i = 0; i < ZXTAPE_HEADER.length; i++) {
if (inpbuf[i] != ZXTAPE_HEADER[i]) {
System.out.println("ERR_NOT_TZX");
return null;
}
}
// ...check TZX version is supported
int cdt_major = inpbuf[8];
if (cdt_major == 0) {
System.out.println("ERR_TZX_UNSUPPORTED");
return null;
}
// Count blocks
currentBlock = 0;
numBlocks = countBlocks(null);
if (numBlocks < 0) {
System.out.println("ERR_TZX_UNSUPPORTED");
return null;
}
// Store the start positions of all blocks
blockStart = new int[numBlocks];
ids = new String[numBlocks + 1];
blocks = new int[numBlocks + 1];
countBlocks(blockStart);
// Initialise WAV format output manager, initially in test mode
// @note: if you ever need to support a different output format,
// create a new CDT2WAVxyzOutput class and switch it in here.
output = new CDT2WAVWAVOutput(frequency);
// Run first pass in test mode to determine data length
debug = false;
convertPass(output);
// If the first pass succeeded, allocate an output buffer
// of the correct length to hold the output data
int dataLength = output.outputTell();
byte[] data = null;
if (dataLength > 0) {
// Run second pass to generate data and store in output buffer
data = new byte[dataLength];
output.setOutputBuffer(data);
convertPass(output);
}
// Return data buffer
return data;
}
public int getBlock(int id) {
if (blocks != null) {
return blocks[id];
}
return 0;
}
public String getID(int id) {
String ret = null;
{
switch (id) {
case 0x10:
ret = "Turbo data II";
break;
case 0x11:
ret = "Turbo data";
break;
case 0x12:
ret = "Pure tone";
break;
case 0x13:
ret = "Sequence of pulses";
break;
case 0x14:
ret = "Pure Data";
break;
case 0x15:
ret = "Direct recording";
break;
case 0x20:
ret = "Pause";
break;
case 0x21:
ret = "Group Start";
break;
case 0x22:
ret = "Group End";
break;
case 0x23:
ret = "Jump relative";
break;
case 0x24:
ret = "Loop Start";
break;
case 0x25:
ret = "Loop End";
break;
case 0x26:
ret = "Call Sequence";
break;
case 0x27:
ret = "Return from Sequence";
break;
case 0x2A:
ret = "Stop tape";
break;
case 0x33:
ret = "Hardware Info";
break;
default:
ret = "Unknown block";
}
}
return ret;
}
}
Die hauptsächliche Klasse, welche die Impulse erzeugt.
Die beiden anderen Klassen:
package jemu.core.device.tape;
/**
* Title: JavaCPC
* Description: The Java Amstrad CPC Emulator
* Copyright: Copyright (c) 2006-2010
* Company:
* @author
* @version 6.8
*/
/**
* Generic wave data output manager
* Converted from "TZX to WAV Converter v0.2 (C) 2006 Francisco Javier Crespo <tzx2wav@ya.com>"
*
* @author John Girvin
*
*/
public abstract class CDT2WAVBaseOutput {
//
// CONSTANTS
//
private static final int LOAMP = 0x26; // Low Level Amplitude (-3 dB)
private static final int HIAMP = 0xDA; // High Level Amplitude (-3 dB)
private static final int NOAMP = 0x80; // Silent Amplitude
//
// INSTANCE VARIABLES
//
private double z80_freq = 3500000.0; // Z80 frequency in Hz
private byte[] buf = null; // output buffer
private int bufPos = 0;
private double frequency = 44100.0; // wave data frequency
private double cycle = 0.0; // Z80 cycles per wave sample
private int amp = LOAMP; // current amplitude
/**
* Create an output manager in "test mode", where no output data
* is generated but all other processing is the same.
*
* @param freq - frequency of output data to pretend-generate
*/
public CDT2WAVBaseOutput(int freq) {
super();
this.buf = null;
this.bufPos = 0;
this.frequency = freq;
/* if (freq == 44100) {
z80_freq = 3400000.0;
} else if (freq == 22050) {
z80_freq = 3494400.0;
}*/
this.cycle = frequency / z80_freq;
init();
}
/**
* Dereference any allocated resources. Do not call this object again
* after calling dispose().
*/
public void dispose() {
buf = null;
}
//
// Concrete implementation interface
//
protected abstract void init();
protected abstract void write(int numsamples);
protected abstract void stop();
/**
* Get the sample frequency to use when generating output
*
* @return frequency in Hz
*/
protected double getFrequency() {
return frequency;
}
/**
* Set the current position in the output buffer.
*
* @return buffer position
*/
protected void outputSeek(int pos) {
bufPos = pos;
}
/**
* Return the current position in the output buffer.
* This is valid even if the output manager is in "test" mode.
*
* @return buffer position
*/
protected int outputTell() {
return bufPos;
}
/**
* Write a byte to the output buffer
*
* @param b - the byte to write
*/
protected final void outputByte(byte b) {
if (buf != null) {
buf[bufPos] = b;
}
bufPos++;
}
/**
* Write bytes to the output buffer
*
* @param b - the byte to write
* @param count - number of times to write the byte
*/
protected final void outputByte(byte b, int count) {
if (buf != null) {
int p = bufPos;
for (int i = 0; i < count; i++) {
buf[p++] = b;
}
}
bufPos += count;
}
//
// PUBLIC INTERFACE
//
/**
* Reset the output buffer. If the supplied buffer is null, the
* output manager will operate in test mode.
*
* @param buf - output buffer
*/
public void setOutputBuffer(byte[] buf) {
this.buf = buf;
this.bufPos = 0;
}
/**
* Convert a sampling value in Z80 T-States to number of samples for wave output
*
* @param tstates - number of Z80 T-States
* @return Number of wave samples at current frequency
*/
public int samples(int tstates) {
return ((int) (0.5 + (cycle * (double) tstates)));
}
/**
* Sets the sign of the wave
*
* @param high - true to set the wave to high amplitude
*/
public void setAmp(boolean high) {
amp = (high ? HIAMP : LOAMP);
}
/**
* Sets the sign of the wave to LO
*/
public void setAmpLow() {
amp = LOAMP;
}
/**
* Sets the sign of the wave to Silent
*/
public void setAmpNo() {
amp = NOAMP;
}
/**
* Toggles the sign of the wave
* TODO: WHOLE CONCEPT TO BE RECODED IN ToggleSgn();
*/
public void toggleAmp() {
if (isLowAmp()) {
amp = HIAMP;
} else {
amp = LOAMP;
}
}
/**
* Is the wave sign currently low amplitude?
*
* @return boolean - true iff the wave sign currently low amplitude
*/
protected boolean isLowAmp() {
return (amp == LOAMP);
}
/**
* Is the wave sign currently silent amplitude?
*
* @return boolean - true iff the wave sign currently silent amplitude
*/
protected boolean isNoAmp() {
return (amp == NOAMP);
}
/**
* Generate wave data for "len" samples.
*
* @param numsamples
*/
public void play(int numsamples) {
write(numsamples);
}
/**
* Waits for a number of milliseconds
*
* @param ms - number of milliseconds
*/
public void pause(int ms) {
int p;
//if (curr!=(numblocks-1))
//{
p = (int) ((((double) ms) * frequency) / 1000.0);
play(p);
//}
}
}
Und:
package jemu.core.device.tape;
/**
* Title: JavaCPC
* Description: The Java Amstrad CPC Emulator
* Copyright: Copyright (c) 2006-2010
* Company:
* @author
* @version 6.8
*/
/**
* WAV file format output manager
* Converted from "TZX to WAV Converter v0.2 (C) 2006 Francisco Javier Crespo <tzx2wav@ya.com>"
*
* Nice WAV format info in this web site:
* http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
*
* @author John Girvin
*
*/
public class CDT2WAVWAVOutput extends CDT2WAVBaseOutput {
//
// INSTANCE VARIABLES
//
private WAVHeader wavHeader;
//
// Inner class to represent WAV file header data
//
public class WAVHeader {
// header data members
public int ChunkID;
public int ChunkSize;
public int Format;
public int fmtChunkID;
public int fmtChunkSize;
public short AudioFormat;
public short NumChannels;
public int SampleRate;
public int ByteRate;
public short BlockAlign;
public short BitsPerSample;
public int Subchunk2ID;
public int Subchunk2Size;
/**
* Write a 32 bit value to the output
* @param data
*/
private void writeInt(int data) {
outputByte((byte) (data & 0xff));
outputByte((byte) ((data >> 8) & 0xff));
outputByte((byte) ((data >> 16) & 0xff));
outputByte((byte) ((data >> 24) & 0xff));
}
/**
* Write a 16 bit value to the output
* @param data
*/
private void writeShort(short data) {
outputByte((byte) (data & 0xff));
outputByte((byte) ((data >> 8) & 0xff));
}
/**
* Write header content to the output
*/
public void write() {
// header goes at start of output
int oldPos = outputTell();
outputSeek(0);
// output the header fields
writeInt(ChunkID);
writeInt(ChunkSize);
writeInt(Format);
writeInt(fmtChunkID);
writeInt(fmtChunkSize);
writeShort(AudioFormat);
writeShort(NumChannels);
writeInt(SampleRate);
writeInt(ByteRate);
writeShort(BlockAlign);
writeShort(BitsPerSample);
writeInt(Subchunk2ID);
writeInt(Subchunk2Size);
// reset back to previous position
outputSeek(oldPos);
}
}
/**
* @see CDT2WAVBaseOutput
*/
public CDT2WAVWAVOutput(int freq) {
super(freq);
}
/**
* @see CDT2WAVBaseOutput.dispose
*/
public void dispose() {
super.dispose();
wavHeader = null;
}
/**
* Prepare for output
*/
protected void init() {
// Initialise WAV file header for 8-bit mono output 95 26 CD 00
wavHeader = new WAVHeader();
wavHeader.ChunkID = 0x46464952; // "RIFF" ID
//wavHeader.ChunkSize = 0xCD2695;
wavHeader.ChunkSize = 0;
wavHeader.Format = 0x45564157; // "WAVE" ID
wavHeader.fmtChunkID = 0x20746D66; // "fmt " ID
wavHeader.fmtChunkSize = 16;
wavHeader.AudioFormat = 1; // PCM Linear Quantization
wavHeader.SampleRate = (int) getFrequency();
wavHeader.Subchunk2ID = 0x61746164; // "data" ID
wavHeader.Subchunk2Size = 0;
wavHeader.NumChannels = 1;
wavHeader.BitsPerSample = 8;
wavHeader.ByteRate = (int) getFrequency();
wavHeader.BlockAlign = 1;
// Send WAV header to the output
// wavHeader.write();
}
/**
* Generate WAV data for "numsamples" samples.
* @param numsamples
*/
protected void write(int numsamples) {
// Update data length in WAV header
wavHeader.Subchunk2Size = (outputTell() + numsamples) - 44;
wavHeader.ChunkSize = 36 + wavHeader.Subchunk2Size;
// Write samples at current amplitude
byte sample;
if (isNoAmp()) {
sample = (byte) 0x80;
} else {
sample = (isLowAmp() ? (byte) 0x26 : (byte) 0xDA);
}
outputByte(sample, numsamples);
}
/**
* Finalise output.
*/
protected void stop() {
// Write updated WAV header to the output
wavHeader.write();
// seriously?
//jemu.system.cpc.CPC.recordcount = bufPos;
}
}
Ist also eine Menge WirrWarr!
(Übrigens reicht dieser Code alleine nicht zum Kompilieren, da JavaCPC keine direkten WAV erzeugt, sondern in einen internen Buffer schreibt)
Den kompletten!!! Sourcecode von JavaCPC kann man übrigens immer (ziemlich aktuell) hier einsehen:
http://javacpc.svn.sourceforge.net/viewvc/javacpc/JavaCPC/src/jemu/
(jemu/ sind die Emulatorspezifischen Dinge drin, drum hab ich den hier angegeben. Du kannst aber auch noch in unteren Ebenen browsen.)
hmm..., ich versteht den zusammenhang zwischen der zeit für 1 bit und der frequenz von der wav 44100 nicht, welche als ton zum cpc gesendet wird für eine normale baudübertragung.
1 bit muss ja eine bestimmte länge , bzw bei 44100 oftmal genug gesendet werden, damit der cpc dieses
als 1 bit erkennt.
klär mich hier mal auf mit dem zeitverhalten.
danke.
Man reiht einfach genügend "pulse's" zusammen, welche dann irgendwann ein bit ergeben...
die möchte ich gerne mal wissen, wie das mit dem 44100hz und der baudzahl zb zusammenhängt.
wie oft die pulse zb für 1 bit gesendet werden müssen und wie lang ein pulse ist?
gruss
Die CDT-Unterstützung von JavaCPC wurde nicht von mir programmiert, weil ich da genausowenig Ahnung von habe.
(Juhuuuu!!! es gibt tatsächlich nette Menschen, die mir bei JavaCPC helfen)
Ich habe nur die Port-Anbindung und die Playback Routine programmiert (Welche allerdings die WAV verwendet)
Ich schicke eine bestimmte Anzahl von WAV-pulses je CPU cycles. Diese Anzahl variiert je nach Frequenz des WAV.
tape_delay = 1050000 / (freq);
freq beinhalted die Frequenz (44100 z.B.)
Also schaue ich in der CPC-cycle funktion nach und mach in etwa soetwas hier:
tapecount++;
if (tapecount > tape_delay){
send next WAV byte to Port
}
Der CPC selbst "cycled" 1000000x pro Sekunde.
Ich verwende allerdings für das Delay 1050000 weil Speedlock sonst herummuckt.
Weiter kann ich Dir dazu aber nicht helfen.
jup, hört sich gut an. danke.
ein frage versuch noch mal zuklären:
die blocks werden ja noch in segmente zu 256 byte aufgeteilt.
am ende vom jedem segment ist ein checksummenbyte von den 256 byte.
wie errechnet sich das checksummenbyte vom segment?
wenn du das noch rauskriegst, wäre ich dir dankbar.
gruss.
Quote from: funkheld on 12:01, 27 October 10
jup, hört sich gut an. danke.
ein frage versuch noch mal zuklären:
die blocks werden ja noch in segmente zu 256 byte aufgeteilt.
am ende vom jedem segment ist ein checksummenbyte von den 256 byte.
wie errechnet sich das checksummenbyte vom segment?
wenn du das noch rauskrigst, wäre ich dir dankbar.
gruss.
http://www.cpctech.org.uk/docs/os.asm
"crc"
http://www.cpctech.org.uk/download/2cdt.zip
Checksum sind ein polynom. Es gibt ein 16-bit Nummer mit 0x0ffff initialisieren . Verwenden das polynom jeden Byte.
(Checksum is calculated with a polynomial. Checksum initialised to 0x0ffff. Then use polynomial for every byte.
See source in 2cdt and operating system dissassembly).
jup, danke.
gruss
Mal DIE doofe Frage:
Was hast denn vor?
es gibt keine doofen fragen, nur unglückliche gestellte fragen... :) .
hmmm..., reine neugierde und ich habe zeit....und macht spass.
ist ja schliesslich ein hobby... :)
bin aber noch nicht so dahinter gestiegen, weil ich java nicht so kenne.
mich würde auch mal das cpm 2.2 auf den cpc464 auf einem tape(cdt) interessieren.
beim kc87 drüben haben die es ja in den achtzigern geschafft, weil sie eben in der disk-technik 120 jahre zurücklagen... :P
ist aber schon ein kunststück.
gruss
Quote from: funkheld on 18:35, 27 October 10
es gibt keine doofen fragen, nur unglückliche gestellte fragen... :) .
hmmm..., reine neugierde und ich habe zeit....und macht spass.
ist ja schliesslich ein hobby... :)
bin aber noch nicht so dahinter gestiegen, weil ich java nicht so kenne.
mich würde auch mal das cpm 2.2 auf den cpc464 auf einem tape(cdt) interessieren.
beim kc87 drüben haben die es ja in den achtzigern geschafft, weil sie eben in der disk-technik 120 jahre zurücklagen... :P
ist aber schon ein kunststück.
gruss
Der AMSDOS-ROM habe der BIOS Funktions fur cpm 2.2.
Hier is ein unfertig Auflistung:
http://www.cpctech.org.uk/docs/amsdos.asm
Der CPC spezifisch COM-Programm treten das AMSDOS-Funktionen.
Dieses ROM ist intern der CPC6128 und die DDI-1 Disketten-Interface.
So wann du machen ein cpm2.2 fur CPC464 nach Kassetten/tape, du must ein neuer BIOS machen.
Ohne AMSDOS-ROM du habe kleine TPA weil Ihr BIOS-Funktion setzen ins RAM.
(Englisch: "The AMSDOS ROM has the BIOS functions. Here is an incomplete listing. The CPC specific COM programs call into the functions in the AMSDOS ROM. The ROM is inside the CPC6128 and the DDI-1 disk interface. So, if you made a cpm 2.2 for cpc464 on cassette, you must make a new BIOS. Without AMSDOS ROM you have small TPA because you must put your BIOS into RAM".
Ich habe übrigens mal mit den KC Compact ROMs herumgespielt und auch das KCC DOS Rom in JavaCPC eingebaut.
JavaCPC's emulierte Floppy lädt das KCC MicroDos problemlos und man kann damit auch arbeiten!
[youtube=NLJkOUINQ3o]NLJkOUINQ3o[/youtube]
hallo, habe deine gui für das cdt2 benutzt und habe festgestellt, das die baudrate und die ladeadresse nicht eingestellt werden kann. ansonsten tolle sache.
habe mal eine gui in purebasic für mich erstellt, mit baud und adresse, weil ich nur noch mit dem ccz80 progge.
die adresse lege ich immer für ein gesamtes tape fest. man könnte auch noch buttons dran klatschen bei den einzelnen programmen für die startadresse.
wer möchte kann damit auch rumwildern.
hmmm..., mit dem wav erstellen habe ich noch die schwierigkeiten bei dem timing,
grübel...grübel..., da fehlt mir der zusammenhang zb für 1000baud und 44100hz...
wenn mir das einer mal zeichnerisch erklären kann, mit den ms-zeiten dafür.
Global cdtname.s, speed.s,cdtordner.s,edit_text.s,speed_text.s,adresse.s,adresse_text.s
Dim dateiname.s ( 8 )
Dim satz.s ( 8 )
Dim dummyname.s ( 8 )
Dim satz.s ( 8 )
Define.l Event, EventWindow, EventGadget, EventType, EventMenu
Enumeration
#Window_0
#Button_0
#Button_1
#text_0
#text_1
#text_2
#text_3
#text_4
#editor_0
#String_name
#String_speed
#String_adresse
#String_0
#String_1
#String_2
#String_3
#String_4
#String_5
#String_6
#String_7
#String_00
#String_11
#String_22
#String_33
#String_44
#String_55
#String_66
#String_77
#Button_r0
#Button_r1
#Button_r2
#Button_r3
#Button_r4
#Button_r5
#Button_r6
#Button_r7
EndEnumeration
OpenWindow(#Window_0, 400, 150, 600,550, "Window_0", #PB_Window_SystemMenu|#PB_Window_SizeGadget|#PB_Window_MinimizeGadget|#PB_Window_TitleBar)
ButtonGadget(#Button_0, 10,35, 80, 25, "erstelle tape")
TextGadget(#text_0, 120, 20, 50, 20, "tape-name")
TextGadget(#text_1, 270, 20, 200, 20, "tape-speed: 0 / 1 oder baud: 100 - 3000")
StringGadget(#String_name, 120, 35, 140, 20, "")
StringGadget(#String_speed, 270, 35, 80, 20, "1")
TextGadget(#text_2, 120, 105, 100, 20, "startname im tape")
TextGadget(#text_3, 270, 105, 100, 20, "ordner mit name")
TextGadget(#text_4, 120, 60, 70, 20, "load-adresse")
StringGadget(#String_adresse, 120, 75, 80, 20, "")
abstd=60
StringGadget(#String_0, 120, 60+abstd, 140, 20, "")
StringGadget(#String_00, 270, 60+abstd, 200, 20, "")
StringGadget(#String_1, 120, 85+abstd, 140, 20, "")
StringGadget(#String_11, 270, 85+abstd, 200, 20, "")
StringGadget(#String_2, 120, 110+abstd, 140, 20, "")
StringGadget(#String_22, 270,110+abstd, 200, 20, "")
StringGadget(#String_3, 120, 135+abstd, 140, 20, "")
StringGadget(#String_33, 270, 135+abstd, 200, 20, "")
StringGadget(#String_4, 120, 160+abstd, 140, 20, "")
StringGadget(#String_44, 270,160+abstd, 200, 20, "")
StringGadget(#String_5, 120, 185+abstd, 140, 20, "")
StringGadget(#String_55, 270, 185+abstd, 200, 20, "")
StringGadget(#String_6, 120, 210+abstd, 140, 20, "")
StringGadget(#String_66, 270,210+abstd, 200, 20, "")
StringGadget(#String_7, 120, 235+abstd, 140, 20, "")
StringGadget(#String_77, 270, 235+abstd, 200, 20, "")
ButtonGadget(#Button_r0, 480,60+abstd, 20,20, "1")
ButtonGadget(#Button_r1, 480,85+abstd, 20,20, "2")
ButtonGadget(#Button_r2, 480,110+abstd, 20,20, "3")
ButtonGadget(#Button_r3, 480,135+abstd, 20,20, "4")
ButtonGadget(#Button_r4, 480,160+abstd, 20,20, "5")
ButtonGadget(#Button_r5, 480,185+abstd, 20,20, "6")
ButtonGadget(#Button_r6, 480,210+abstd, 20,20, "7")
ButtonGadget(#Button_r7, 480,235+abstd, 20,20, "8")
EditorGadget(#Editor_0, 120, 335, 400,180)
cdtordner="d:\cpc\make-cdt\2cdt"
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Gadget
EventGadget = EventGadget()
EventType = EventType()
If EventGadget = #Button_r0
dateiname(0)=OpenFileRequester("Load Bindatei","","Bindatei (*.bin",0)
SetGadgetText(#String_0,GetFilePart(dateiname(0)))
SetGadgetText(#String_00,dateiname(0))
EndIf
If EventGadget = #Button_r1
dateiname(1)=OpenFileRequester("Load Bindatei","","Bindatei (*.bin",0)
SetGadgetText(#String_1,GetFilePart(dateiname(1)))
SetGadgetText(#String_11,dateiname(1))
EndIf
If EventGadget = #Button_r2
dateiname(2)=OpenFileRequester("Load Bindatei","","Bindatei (*.bin",0)
SetGadgetText(#String_2,GetFilePart(dateiname(2)))
SetGadgetText(#String_22,dateiname(2))
EndIf
If EventGadget = #Button_r3
dateiname(3)=OpenFileRequester("Load Bindatei","","Bindatei (*.bin",0)
SetGadgetText(#String_3,GetFilePart(dateiname(3)))
SetGadgetText(#String_33,dateiname(3))
EndIf
If EventGadget = #Button_r4
dateiname(4)=OpenFileRequester("Load Bindatei","","Bindatei (*.bin",0)
SetGadgetText(#String_4,GetFilePart(dateiname(4)))
SetGadgetText(#String_44,dateiname(4))
EndIf
If EventGadget = #Button_r5
dateiname(5)=OpenFileRequester("Load Bindatei","","Bindatei (*.bin",0)
SetGadgetText(#String_5,GetFilePart(dateiname(5)))
SetGadgetText(#String_55,dateiname(5))
EndIf
If EventGadget = #Button_r6
dateiname(6)=OpenFileRequester("Load Bindatei","","Bindatei (*.bin",0)
SetGadgetText(#String_6,GetFilePart(dateiname(6)))
SetGadgetText(#String_66,dateiname(6))
EndIf
If EventGadget = #Button_r7
dateiname(7)=OpenFileRequester("Load Bindatei","","Bindatei (*.bin",0)
SetGadgetText(#String_7,GetFilePart(dateiname(7)))
SetGadgetText(#String_77,dateiname(7))
EndIf
If EventGadget = #Button_0
cdtname=GetGadgetText(#String_name)
speed=GetGadgetText(#String_speed)
adresse=GetGadgetText(#String_adresse)
cdtname=cdtname+".cdt"
ClearGadgetItems(#Editor_0)
edit_text=""
If Val(speed) = 0
speed_text=" -s" + " "+ "0"
ElseIf Val(speed) = 1
speed_text=" -s" + " "+ "1"
ElseIf Val(speed)> 99 And Val(speed) < 3001
speed_text=" -b" + " "+ speed
Else
edit_text+"speed stimmt nicht"+Chr(10)
SetGadgetText(#Editor_0, edit_text)
Goto schluss
EndIf
If Val(adresse) => 8192
adresse_text=" -L " + adresse
Else
adresse_text=""
EndIf
If dateiname(0)
dummyname(0)=GetGadgetText(#String_0)
satz(0)= adresse_text + speed_text + " " + "-n -r "+ dummyname(0) + " " + dateiname(0) +" "+ cdtname
RunProgram(cdtordner,satz(0) ,"",#PB_Program_Hide)
edit_text+satz(0)+Chr(10)
SetGadgetText(#Editor_0, edit_text)
EndIf
If dateiname(1)
Delay(100)
dummyname(1)=GetGadgetText(#String_1)
satz(1)= adresse_text + speed_text + " " + "-r "+ dummyname(1) + " " + dateiname(1) +" "+ cdtname
RunProgram(cdtordner,satz(1) ,"",#PB_Program_Hide)
edit_text+satz(1)+Chr(10)
SetGadgetText(#Editor_0, edit_text)
EndIf
If dateiname(2)
Delay(100)
dummyname(2)=GetGadgetText(#String_2)
satz(2)= " -m 0 "+ adresse_text + speed_text + " " + "-r "+ dummyname(2) + " " + dateiname(2) +" "+ cdtname
RunProgram(cdtordner,satz(2) ,"",#PB_Program_Hide)
edit_text+satz(2)+Chr(10)
SetGadgetText(#Editor_0, edit_text)
EndIf
If dateiname(3)
Delay(100)
dummyname(3)=GetGadgetText(#String_3)
satz(3)= " -m 0 "+ adresse_text + speed_text + " " + "-r "+ dummyname(3) + " " + dateiname(3) +" "+ cdtname
RunProgram(cdtordner,satz(3) ,"",#PB_Program_Hide)
edit_text+satz(3)+Chr(10)
SetGadgetText(#Editor_0, edit_text)
EndIf
If dateiname(4)
Delay(100)
dummyname(4)=GetGadgetText(#String_4)
satz(4)= " -m 0 "+ adresse_text + speed_text + " " + "-r "+ dummyname(4) + " " + dateiname(4) +" "+ cdtname
RunProgram(cdtordner,satz(4) ,"",#PB_Program_Hide)
edit_text+satz(4)+Chr(10)
SetGadgetText(#Editor_0, edit_text)
EndIf
If dateiname(5)
Delay(100)
dummyname(5)=GetGadgetText(#String_5)
satz(5)= " -m 0 "+ adresse_text + speed_text + " " + "-r "+ dummyname(5) + " " + dateiname(5) +" "+ cdtname
RunProgram(cdtordner,satz(5) ,"",#PB_Program_Hide)
edit_text+satz(5)+Chr(10)
SetGadgetText(#Editor_0, edit_text)
EndIf
If dateiname(6)
Delay(100)
dummyname(6)=GetGadgetText(#String_6)
satz(6)= " -m 0 "+ adresse_text + speed_text + " " + "-r "+ dummyname(6) + " " + dateiname(6) +" "+ cdtname
RunProgram(cdtordner,satz(6) ,"",#PB_Program_Hide)
edit_text+satz(6)+Chr(10)
SetGadgetText(#Editor_0, edit_text)
EndIf
If dateiname(7)
Delay(100)
dummyname(7)=GetGadgetText(#String_7)
satz(7)= " -m 0 "+ adresse_text + speed_text + " " + "-r "+ dummyname(7) + " " + dateiname(7) +" "+ cdtname
RunProgram(cdtordner,satz(7) ,"",#PB_Program_Hide)
edit_text+satz(7)+Chr(10)
SetGadgetText(#Editor_0, edit_text)
EndIf
schluss:
EndIf
Case #PB_Event_CloseWindow
EventWindow = EventWindow()
If EventWindow = #Window_0
CloseWindow(#Window_0)
Break
EndIf
EndSelect
ForEver