diff --git a/configure.ac b/configure.ac index 7401091..450b10d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ([2.61]) -AC_INIT([liba8cas], [1.5.0],,,[http://a8cas.sourceforge.net/]) +AC_INIT([liba8cas], [1.5.1],,,[http://a8cas.sourceforge.net/]) # foreign, because we don't have a ChangeLog file AM_INIT_AUTOMAKE([foreign silent-rules]) diff --git a/include/a8cas.h b/include/a8cas.h index 4992867..be7c845 100644 --- a/include/a8cas.h +++ b/include/a8cas.h @@ -181,6 +181,8 @@ A8CAS_API int A8CAS_read_audio(A8CAS_FILE *file, void *samples, unsigned int num A8CAS_API int A8CAS_write_audio(A8CAS_FILE *file, A8CAS_signal const *sig, void *samples); +A8CAS_API int A8CAS_write_with_audio(A8CAS_FILE *file, A8CAS_signal const *sig, void *samples, unsigned int *audio_samples); + /* ---- Block-navigation functions ---- */ /* Returns current block/sample number (counted from 0). */ @@ -231,7 +233,8 @@ typedef enum A8CAS_param { A8CAS_PARAM_SILENCE_LEVEL, /* Value type: float */ A8CAS_PARAM_CROSSTALK_VOLUME, /* Value type: float */ A8CAS_PARAM_MIN_BAUDRATE, /* Value type: unsigned int */ - A8CAS_PARAM_MAX_BAUDRATE /* Value type: unsigned int */ + A8CAS_PARAM_MAX_BAUDRATE, /* Value type: unsigned int */ + A8CAS_PARAM_PWM_ENCODE /* Value type: int */ } A8CAS_param; /* Returns 0 on success, else call A8CAS_error() to get an error code: diff --git a/src/a8cas.c b/src/a8cas.c index ee9d303..f21077a 100644 --- a/src/a8cas.c +++ b/src/a8cas.c @@ -173,8 +173,16 @@ int A8CAS_write(A8CAS_FILE *file, A8CAS_signal const *sig) return (*file->write_func)(file, sig); } +/* Deprecated */ int A8CAS_write_audio(A8CAS_FILE *file, A8CAS_signal const *sig, void *samples) { + unsigned int dummy_number; + + return A8CAS_write_with_audio(file, sig, samples, &dummy_number); +} + +int A8CAS_write_with_audio(A8CAS_FILE *file, A8CAS_signal const *sig, void *samples, unsigned int *audio_samples) +{ /* Signals of length 0 cause problems in CAS_ENCODE. So they are ignored a bit earlier. */ if (sig->length == 0) @@ -187,7 +195,7 @@ int A8CAS_write_audio(A8CAS_FILE *file, A8CAS_signal const *sig, void *samples) case A8CAS_FORMAT_FSK: return FORMAT_FSK_write_audio(file, sig, samples); case A8CAS_FORMAT_SNDFILE: - return FORMAT_SNDFILE_write_audio(file, sig, samples); + return FORMAT_SNDFILE_write_with_audio(file, sig, samples, audio_samples); default: /* case A8CAS_FORMAT_RAW */ return FORMAT_RAW_write_audio(file, sig, samples); } @@ -440,6 +448,11 @@ int A8CAS_switch_PWM_decoding(A8CAS_FILE *file, int on) return A8CAS_set_param(file, A8CAS_PARAM_PWM_DECODE, &on); } +int A8CAS_switch_PWM_encoding(A8CAS_FILE *file, int on) +{ + return A8CAS_set_param(file, A8CAS_PARAM_PWM_ENCODE, &on); +} + int A8CAS_set_PWM_tolerance(A8CAS_FILE *file, unsigned short int tolerance) { return A8CAS_set_param(file, A8CAS_PARAM_PWM_TOLERANCE, &tolerance); diff --git a/src/cas_decode.c b/src/cas_decode.c index 01d5aee..435db48 100644 --- a/src/cas_decode.c +++ b/src/cas_decode.c @@ -405,7 +405,7 @@ unsigned int CAS_DECODE_read_signals(CAS_DECODE_t *decode, A8CAS_signal *sigs, u { if ((*decode->read_func)(decode, &sigs[written++]) != 0); - return 0; + return 0; } } } diff --git a/src/cas_encode.h b/src/cas_encode.h index 28c1f73..f0166eb 100644 --- a/src/cas_encode.h +++ b/src/cas_encode.h @@ -26,6 +26,8 @@ #include "a8cas.h" #include "pokey_deserialize.h" +#include "pwm.h" + enum { CAS_ENCODE_DATA_BUF_SIZE = 65535 }; /* CAS_ENCODE_FSK_BUF_SIZE must be an odd number. */ @@ -64,6 +66,11 @@ struct CAS_ENCODE_t { int (*process_byte_func)(CAS_ENCODE_t*, uint8_t, double); int (*process_signal_func)(CAS_ENCODE_t*, uint8_t, double); + + int (*write_pwms_block_func)(A8CAS_FILE*, uint16_t, int, int); + int (*write_pwmd_block_func)(A8CAS_FILE*, unsigned int, unsigned int, uint8_t const *, uint16_t); + int (*write_pwmc_block_func)(A8CAS_FILE*, uint16_t, pwmc_buffer *, uint16_t); + int (*write_pwml_block_func)(A8CAS_FILE*, uint16_t, uint16_t *, uint16_t); }; /* Sets default values for encode adjustments */ diff --git a/src/format_cas.c b/src/format_cas.c index 3c5767b..de5cfe0 100644 --- a/src/format_cas.c +++ b/src/format_cas.c @@ -37,6 +37,7 @@ #include "format_stdio_based.h" #include "io.h" #include "read_audio.h" +#include "pwm.h" /*#define REPORT_BLOCKS*/ @@ -475,6 +476,24 @@ static int write_baud_block(A8CAS_FILE *file, uint16_t baudrate) return write_cas_header(file, &header); } +static int write_pwms_block(A8CAS_FILE *file, uint16_t rate, int lsb_first, int rising_edge_first) +{ + file_specific *data = (file_specific*) file->specific_data; + CAS_HEADER_t header; + int result; + header.type = BLOCK_TYPE_PWMS; + header.length = 2; + header.aux = (lsb_first << 2) + (rising_edge_first ? 2 : 1); + result = write_cas_header(file, &header); + if (result != 0) + return 1; + if (fwrite(&rate, sizeof(uint16_t), 1, data->file) != 1) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + return result; +} + static int write_data_block(A8CAS_FILE *file, uint16_t irg_ms, uint8_t const *buffer, uint16_t length) { file_specific *data = (file_specific*) file->specific_data; @@ -492,12 +511,29 @@ static int write_data_block(A8CAS_FILE *file, uint16_t irg_ms, uint8_t const *bu return 0; } -static int write_fsk_block(A8CAS_FILE *file, uint16_t irg_ms, uint16_t *buffer, uint16_t length) +static int write_pwmd_block(A8CAS_FILE *file, unsigned int length_0, unsigned int length_1, uint8_t const *buffer, uint16_t length) +{ + file_specific *data = (file_specific*) file->specific_data; + CAS_HEADER_t header; + header.type = BLOCK_TYPE_PWMD; + header.length = length; + header.aux = length_0 + (length_1 << 8); + if (write_cas_header(file, &header) != 0) + return 1; + + if (fwrite(buffer, sizeof(uint8_t), length, data->file) != length) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + return 0; +} + +static int write_various_signals__block(A8CAS_FILE *file, uint32_t type, uint16_t irg_ms, uint16_t *buffer, uint16_t length) { file_specific *data = (file_specific*) file->specific_data; CAS_HEADER_t header; int i; - header.type = BLOCK_TYPE_FSK; + header.type = type; header.length = length * 2; header.aux = irg_ms; if (write_cas_header(file, &header) != 0) @@ -514,6 +550,38 @@ static int write_fsk_block(A8CAS_FILE *file, uint16_t irg_ms, uint16_t *buffer, return 0; } +static int write_fsk_block(A8CAS_FILE *file, uint16_t irg_ms, uint16_t *buffer, uint16_t length) +{ + return write_various_signals__block(file, BLOCK_TYPE_FSK, irg_ms, buffer, length); +} + +static int write_pwmc_block(A8CAS_FILE *file, uint16_t irg_ms, pwmc_buffer *buffer, uint16_t length) +{ + file_specific *data = (file_specific*) file->specific_data; + CAS_HEADER_t header; + int i; + header.type = BLOCK_TYPE_PWMC; + header.length = length * 3; + header.aux = irg_ms; + if (write_cas_header(file, &header) != 0) + return 1; + + /* Convert the buffer to proper endianness. */ + for (i = 0; i < length; i += 2) + buffer[i+1] = LE_SHORT(buffer[i+1]); + + if (fwrite(buffer, sizeof(uint8_t) + sizeof(uint16_t), length, data->file) != length) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + return 0; +} + +static int write_pwml_block(A8CAS_FILE *file, uint16_t irg_ms, uint16_t *buffer, uint16_t length) +{ + return write_various_signals__block(file, BLOCK_TYPE_PWMC, irg_ms, buffer, length); +} + /* ------------------------------------------------------------------------ * * Write functions * * ------------------------------------------------------------------------ */ @@ -896,6 +964,10 @@ int FORMAT_CAS_open(A8CAS_FILE *file, char const *path, A8CAS_info *info) data->cas_encode.write_baud_block_func = &write_baud_block; data->cas_encode.write_data_block_func = &write_data_block; data->cas_encode.write_fsk_block_func = &write_fsk_block; + data->cas_encode.write_pwms_block_func = &write_pwms_block; + data->cas_encode.write_pwmd_block_func = &write_pwmd_block; + data->cas_encode.write_pwmc_block_func = &write_pwmc_block; + data->cas_encode.write_pwml_block_func = &write_pwml_block; data->cas_encode.add_block_offset_func = &add_offset_after_write_block; /*data->read_audio.mod_type = MOD_TYPE_FSK;*/ diff --git a/src/format_hex.c b/src/format_hex.c index c5403b0..8f71d55 100644 --- a/src/format_hex.c +++ b/src/format_hex.c @@ -38,6 +38,7 @@ #include "format_stdio_based.h" #include "io.h" #include "read_audio.h" +#include "pwm.h" #define READ_BUF_SIZE 16 #define FSCANF_BUF_SIZE 21 @@ -546,6 +547,19 @@ static int write_baud_block(A8CAS_FILE *file, uint16_t baudrate) return 0; } +static int write_pwms_block(A8CAS_FILE *file, uint16_t rate, int lsb_first, int rising_edge_first) +{ + file_specific *data = (file_specific*) file->specific_data; + if (fprintf(data->file, "pwms %s %s %05u\n", + lsb_first ? "lsb_first" : "msb_first", + rising_edge_first ? "rising_edge_first" : "falling_edge_first", + (unsigned int)rate) < 0) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + return 0; +} + static int write_data_block_bytes(A8CAS_FILE *file, uint16_t irg_ms, uint8_t const *buffer, uint16_t length) { file_specific *data = (file_specific*) file->specific_data; @@ -600,6 +614,44 @@ static int write_standard_block(A8CAS_FILE *file, uint16_t irg_ms, uint8_t const return 0; } +static int write_pwmd_block_bytes(A8CAS_FILE *file, unsigned int length_0, unsigned int length_1, uint8_t const *buffer, uint16_t length) +{ + file_specific *data = (file_specific*) file->specific_data; + unsigned int i; + + if (fprintf(data->file, "pwmd %u %u", (unsigned int)length_0, (unsigned int)length_1) < 0) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + for(i = 0; i < length; i ++) { + if (fprintf(data->file, " %02x", (unsigned int)buffer[i]) == -1) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + } + + return 0; +} + +static int write_pwmd_block(A8CAS_FILE *file, unsigned int length_0, unsigned int length_1, uint8_t const *buffer, uint16_t length) +{ + file_specific *data = (file_specific*) file->specific_data; + unsigned int checksum = 0; + + if (write_pwmd_block_bytes(file, length_0, length_1, buffer, length) != 0) + return 1; + +/* compute checksum */ + checksum = 0; + + if (fprintf(data->file, " ; length=%u, checksum=%u\n", (unsigned int)length, checksum) == -1) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + + return 0; +} + static int write_turbo2600_block(A8CAS_FILE *file, uint16_t irg_ms, uint8_t const *buffer, uint16_t length) { file_specific *data = (file_specific*) file->specific_data; @@ -704,18 +756,18 @@ static int write_data_block(A8CAS_FILE *file, uint16_t irg_ms, uint8_t const *bu return write_standard_block(file, irg_ms, buffer, length); } -static int write_fsk_block(A8CAS_FILE *file, uint16_t irg_ms, uint16_t *buffer, uint16_t length) +static int write_various_signals_block(A8CAS_FILE *file, char *type, uint16_t irg_ms, uint16_t *buffer, uint16_t length) { file_specific *data = (file_specific*) file->specific_data; unsigned long duration = 0; unsigned int i; - if (fprintf(data->file, "fsk %05"PRIu16, irg_ms) < 0) { + if (fprintf(data->file, "%s %05u", type, (unsigned int)irg_ms) < 0) { file->errnum = A8CAS_ERR_FWRITE; return 1; } for(i = 0; i < length; i ++) { - if (fprintf(data->file, " %"PRIu8, buffer[i]) < 0) { + if (fprintf(data->file, " %u", (unsigned int)buffer[i]) == -1) { file->errnum = A8CAS_ERR_FWRITE; return 1; } @@ -730,6 +782,44 @@ static int write_fsk_block(A8CAS_FILE *file, uint16_t irg_ms, uint16_t *buffer, return 0; } +static int write_fsk_block(A8CAS_FILE *file, uint16_t irg_ms, uint16_t *buffer, uint16_t length) +{ + return write_various_signals_block(file, "fsk ", irg_ms, buffer, length); +} + +static int write_pwmc_block(A8CAS_FILE *file, uint16_t irg_ms, pwmc_buffer *buffer, uint16_t length) +{ + file_specific *data = (file_specific*) file->specific_data; + unsigned int i; + + if (fprintf(data->file, "pwmc %05u", (unsigned int)irg_ms) < 0) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + for(i = 0; i < length; i ++) { + if (fprintf(data->file, " %u", (char)buffer[i].length) == -1) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + if (fprintf(data->file, " %u", (unsigned int)buffer[i].count) == -1) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + } + + if (fprintf(data->file, " ; count=%u\n", (unsigned int)length) == -1) { + file->errnum = A8CAS_ERR_FWRITE; + return 1; + } + + return 0; +} + +static int write_pwml_block(A8CAS_FILE *file, uint16_t irg_ms, uint16_t *buffer, uint16_t length) +{ + return write_various_signals_block(file, "pwml", irg_ms, buffer, length); +} + /* ------------------------------------------------------------------------ * * Write functions * * ------------------------------------------------------------------------ */ @@ -1116,6 +1206,10 @@ int FORMAT_HEX_open(A8CAS_FILE *file, char const *path, A8CAS_info *info) data->cas_encode.write_baud_block_func = &write_baud_block; data->cas_encode.write_data_block_func = &write_data_block; data->cas_encode.write_fsk_block_func = &write_fsk_block; + data->cas_encode.write_pwms_block_func = &write_pwms_block; + data->cas_encode.write_pwmd_block_func = &write_pwmd_block; + data->cas_encode.write_pwmc_block_func = &write_pwmc_block; + data->cas_encode.write_pwml_block_func = &write_pwml_block; data->cas_encode.add_block_offset_func = &add_offset_after_write_block; data->read_audio.mod_type = MOD_TYPE_FSK; diff --git a/src/format_sndfile.c b/src/format_sndfile.c index 3ed852b..b33fade 100644 --- a/src/format_sndfile.c +++ b/src/format_sndfile.c @@ -70,6 +70,7 @@ struct file_specific { PWM_DEMOD_t pwm; } demod; int (*advance_demod_func)(file_specific*, short int); + int pwm_mod; /* Fields used while block counting and rewinding */ /* Number of samples already read / number of the next sample @@ -233,7 +234,11 @@ static int specific_write(A8CAS_FILE *file, A8CAS_signal const *sig) { unsigned int free_space = DEFAULT_BUF_SIZE - data->buffer_write_fill; if (samples_to_write >= free_space) { /* wrap */ - FSK_MOD_generate(&data->mod.fsk, data->buffer + data->buffer_write_fill, + if (data->pwm_mod) + PWM_MOD_generate(&data->mod.pwm, data->buffer + data->buffer_write_fill, + free_space, sig->signal); + else + FSK_MOD_generate(&data->mod.fsk, data->buffer + data->buffer_write_fill, free_space, sig->signal); data->buffer_write_fill = DEFAULT_BUF_SIZE; if ((file->errnum = flush_data(data)) != A8CAS_ERR_NONE) @@ -241,7 +246,11 @@ static int specific_write(A8CAS_FILE *file, A8CAS_signal const *sig) data->buffer_write_fill = 0; samples_to_write -= free_space; } else { - FSK_MOD_generate(&data->mod.fsk, data->buffer + data->buffer_write_fill, + if (data->pwm_mod) + PWM_MOD_generate(&data->mod.pwm, data->buffer + data->buffer_write_fill, + samples_to_write, sig->signal); + else + FSK_MOD_generate(&data->mod.fsk, data->buffer + data->buffer_write_fill, samples_to_write, sig->signal); data->buffer_write_fill += samples_to_write; data->file_offset += (sf_count_t)sample_time; @@ -365,7 +374,9 @@ void FORMAT_SNDFILE_set_audio(A8CAS_FILE *file) { file_specific *data = (file_specific*) file->specific_data; + PWM_MOD_init(&data->audio_mod.pwm, file->audio.samplerate, file->audio.bits, 1, 0); FSK_MOD_init(&data->audio_mod.fsk, file->audio.samplerate, file->audio.bits, 1, 0, A8CAS_MARK_FREQ, A8CAS_SPACE_FREQ); + RESAMPLE_init(&data->resample, data->snd_info.samplerate, file->audio.samplerate); } @@ -428,13 +439,40 @@ int FORMAT_SNDFILE_read_audio(A8CAS_FILE *file, void *samples, unsigned int num_ return num_samples; } +/* Deprecated */ int FORMAT_SNDFILE_write_audio(A8CAS_FILE *file, A8CAS_signal const *sig, void *samples) { + unsigned int dummy_number; + + return FORMAT_SNDFILE_write_with_audio(file, sig, samples, &dummy_number); +} + +int FORMAT_SNDFILE_write_with_audio(A8CAS_FILE *file, A8CAS_signal const *sig, void *samples, unsigned int *audio_samples) +{ file_specific *data = (file_specific*) file->specific_data; + short int sample; if ((*file->write_func)(file, sig) != 0) return 1; - FSK_MOD_generate(&data->audio_mod.fsk, samples, sig->length, sig->signal); + + *audio_samples = 0; + for (int i=0; i < sig->length; i++) { + sample = (sig->signal == 1 ? USHRT_MAX : 0); + if (RESAMPLE_next(&data->resample, &sample)) { + (*audio_samples) ++; + while (!data->resample.need) + if (RESAMPLE_next(&data->resample, &sample)) + (*audio_samples) ++; + } + } + + if (*audio_samples > 0) { + if (data->pwm_mod) { + PWM_MOD_generate(&data->audio_mod.pwm, samples, *audio_samples, sig->signal); + } + else + FSK_MOD_generate(&data->audio_mod.fsk, samples, *audio_samples, sig->signal); + } return 0; } @@ -583,9 +621,11 @@ static void reset_before_read_bytes(A8CAS_FILE *file) static int common_read_bytes_after_write(A8CAS_FILE *file) { if (reset_after_write(file) != 0) + return 1; reset_before_read_bytes(file); reset_buffer(file); + return 0; } @@ -733,6 +773,9 @@ int FORMAT_SNDFILE_set_param(A8CAS_FILE *file, A8CAS_param type, void const *val file_specific *data = (file_specific*) file->specific_data; switch (type) { + case A8CAS_PARAM_PWM_ENCODE: + data->pwm_mod = *((int *) value); + break; case A8CAS_PARAM_PWM_DECODE: if (*((int *) value)) { PWM_DEMOD_init(&data->demod.pwm); @@ -778,6 +821,7 @@ int FORMAT_SNDFILE_set_param(A8CAS_FILE *file, A8CAS_param type, void const *val data->demod.fsk.silence_threshold = *((float *)value); break; case A8CAS_PARAM_CROSSTALK_VOLUME: + PWM_MOD_set_volume(&data->audio_mod.pwm, *((float *)value)); FSK_MOD_set_volume(&data->audio_mod.fsk, *((float *)value)); data->crosstalk_volume = *((float *)value) * USHRT_MAX; break; @@ -793,6 +837,9 @@ int FORMAT_SNDFILE_get_param(A8CAS_FILE *file, A8CAS_param type, void *value) file_specific *data = (file_specific*) file->specific_data; switch (type) { + case A8CAS_PARAM_PWM_ENCODE: + *((int *) value) = data->pwm_mod; + break; case A8CAS_PARAM_PWM_DECODE: *((int *) value) = (data->advance_demod_func == &advance_pwm_demod); break; @@ -923,10 +970,15 @@ int FORMAT_SNDFILE_open(A8CAS_FILE *file, char const *path, A8CAS_info *info) data->demod.pwm.tolerance = PWM_DEMOD_DEFAULT_TOLERANCE; data->demod.fsk.silence_threshold = FSK_DEMOD_DEFAULT_SILENCE_THRESHOLD; + data->pwm_mod = 0; + data->crosstalk_volume = USHRT_MAX / 20; FSK_MOD_init(&data->mod.fsk, data->snd_info.samplerate, 16, data->snd_info.channels, data->signal_channel_num, A8CAS_MARK_FREQ, A8CAS_SPACE_FREQ); + PWM_MOD_init(&data->mod.pwm, data->snd_info.samplerate, 16, + data->snd_info.channels, data->signal_channel_num); + return 0; } diff --git a/src/format_sndfile.h b/src/format_sndfile.h index 5ebf959..df957f0 100644 --- a/src/format_sndfile.h +++ b/src/format_sndfile.h @@ -37,6 +37,7 @@ enum { FORMAT_SNDFILE_SEEK_UNIT = A8CAS_SEEK_UNIT_SAMPLES }; void FORMAT_SNDFILE_set_audio(A8CAS_FILE *file); int FORMAT_SNDFILE_read_audio(A8CAS_FILE *file, void *samples, unsigned int num_samples); int FORMAT_SNDFILE_write_audio(A8CAS_FILE *file, A8CAS_signal const *sig, void *samples); +int FORMAT_SNDFILE_write_with_audio(A8CAS_FILE *file, A8CAS_signal const *sig, void *samples, unsigned int *num_samples); unsigned int FORMAT_SNDFILE_read_bytes(A8CAS_FILE *file, unsigned char *bytes, unsigned int size, unsigned int *baudrate); unsigned int FORMAT_SNDFILE_read_signals(A8CAS_FILE *file, A8CAS_signal *sigs, unsigned int size); diff --git a/src/pwm.h b/src/pwm.h new file mode 100644 index 0000000..1178d81 --- /dev/null +++ b/src/pwm.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010 Tomasz Krasuski (see AUTHORS) + * + * This file is part of the A8CAS project which allows to manipulate tape + * images for Atari 8-bit computers. + * + * A8CAS is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * A8CAS is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with A8CAS; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef PWM_H +#define PWM_H + +typedef struct pwmc_buffer { + uint8_t length; + uint16_t count; +} pwmc_buffer; + +#endif /* PWM_H */ diff --git a/src/pwm_mod.c b/src/pwm_mod.c index 7cab802..d9942b3 100644 --- a/src/pwm_mod.c +++ b/src/pwm_mod.c @@ -25,13 +25,21 @@ static void generate_8bit(PWM_MOD_t *mod, void *buffer, unsigned int num_samples, int signal) { + static float const add = (float) UCHAR_MAX / 2.0f; float volume = mod->volume; unsigned int i, j; unsigned char *buf = (unsigned char *)buffer; + static int same_level_len = 0; + static int last_signal=-1; + if (signal!=last_signal) same_level_len = 0; for (i = 0, j = mod->write_channel; i < num_samples; i ++, j += mod->num_channels) { - buf[j] = (unsigned char)((signal ? 1.0f : 0.0f) * volume); + buf[j] = (unsigned char)( same_level_len < 2 * mod->samples_per_ms ? ((signal ? 1.0f : 0.0f) * volume + add) : + same_level_len < 10 * mod->samples_per_ms ? ((signal ? 0.0f : 1.0f) * volume/1.5f + add) : + same_level_len < 10.5f * mod->samples_per_ms ? ((signal ? 1.0f : 0.0f) * volume/2.0f + add) : + same_level_len < 11 * mod->samples_per_ms ? ((signal ? 0.0f : 1.0f) * volume/4.0f) : add); } + last_signal = signal; } static void generate_16bit(PWM_MOD_t *mod, void *buffer, unsigned int num_samples, int signal) @@ -40,10 +48,19 @@ static void generate_16bit(PWM_MOD_t *mod, void *buffer, unsigned int num_sample float volume = mod->volume; unsigned int i, j; short *buf = (short *)buffer; + static int same_level_len = 0; + static int last_signal=-1; + if (signal!=last_signal) same_level_len = 0; for (i = 0, j = mod->write_channel; i < num_samples; i ++, j += mod->num_channels) { - buf[j] = (short)((signal ? 1.0f : -1.0f) * volume + add); + /* make data tails pretty ;) and functional */ + same_level_len++; + buf[j] = (short)( same_level_len < 2 * mod->samples_per_ms ? ((signal ? 1.0f : -1.0f) * volume + add) : + same_level_len < 10 * mod->samples_per_ms ? ((signal ? -1.0f : 1.0f) * volume/1.5f + add) : + same_level_len < 10.5f * mod->samples_per_ms ? ((signal ? 1.0f : -1.0f) * volume/2.0f + add) : + same_level_len < 11 * mod->samples_per_ms ? ((signal ? -1.0f : 1.0f) * volume/4.0f + add) : add); } + last_signal = signal; } void PWM_MOD_init(PWM_MOD_t *mod, unsigned int samplerate, unsigned int bits, unsigned int num_channels, unsigned int write_channel) @@ -54,6 +71,7 @@ void PWM_MOD_init(PWM_MOD_t *mod, unsigned int samplerate, unsigned int bits, un mod->gen_func = &generate_8bit; mod->num_channels = num_channels; mod->write_channel = write_channel; + mod->samples_per_ms = (unsigned int) samplerate/1000; PWM_MOD_set_volume(mod, 1.0f); } diff --git a/src/pwm_mod.h b/src/pwm_mod.h index cafa2a5..c9de579 100644 --- a/src/pwm_mod.h +++ b/src/pwm_mod.h @@ -29,6 +29,7 @@ struct PWM_MOD_t { unsigned int num_channels; unsigned int write_channel; float volume; + unsigned int samples_per_ms; }; void PWM_MOD_init(PWM_MOD_t *mod, unsigned int samplerate, unsigned int bits, unsigned int num_channels, unsigned int write_channel);