libspectrum_error libspectrum_wav_read( libspectrum_tape *tape, const char *filename ) { libspectrum_byte *buffer; size_t length; libspectrum_byte *tape_buffer; size_t tape_length; size_t data_length; libspectrum_tape_block *block = NULL; int frames; /* Our filehandle from libaudiofile */ AFfilehandle handle; /* The track we're using in the file */ int track = AF_DEFAULT_TRACK; if( !filename ) { libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_wav_read: no filename provided - wav files can only be loaded from a file" ); return LIBSPECTRUM_ERROR_LOGIC; } handle = afOpenFile( filename, "r", NULL ); if( handle == AF_NULL_FILEHANDLE ) { libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_wav_read: audiofile failed to open file:%s", filename ); return LIBSPECTRUM_ERROR_LOGIC; } if( afSetVirtualSampleFormat( handle, track, AF_SAMPFMT_UNSIGNED, 8 ) ) { afCloseFile( handle ); libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_wav_read: audiofile failed to set virtual sample format" ); return LIBSPECTRUM_ERROR_LOGIC; } if( afSetVirtualChannels( handle, track, 1 ) ) { afCloseFile( handle ); libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_wav_read: audiofile failed to set virtual channel count" ); return LIBSPECTRUM_ERROR_LOGIC; } length = afGetFrameCount( handle, track ); tape_length = length; if( tape_length%8 ) tape_length += 8 - (tape_length%8); buffer = libspectrum_new0( libspectrum_byte, tape_length * afGetChannels(handle, track) ); frames = afReadFrames( handle, track, buffer, length ); if( frames == -1 ) { libspectrum_free( buffer ); afCloseFile( handle ); libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_wav_read: can't calculate number of frames in audio file" ); return LIBSPECTRUM_ERROR_CORRUPT; } if( !length ) { libspectrum_free( buffer ); afCloseFile( handle ); libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_wav_read: empty audio file, nothing to load" ); return LIBSPECTRUM_ERROR_CORRUPT; } if( frames != length ) { libspectrum_free( buffer ); afCloseFile( handle ); libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_wav_read: read %d frames, but expected %lu\n", frames, (unsigned long)length ); return LIBSPECTRUM_ERROR_CORRUPT; } block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_RAW_DATA ); /* 44100 Hz 79 t-states 22050 Hz 158 t-states */ libspectrum_tape_block_set_bit_length( block, 3500000/afGetRate( handle, track ) ); libspectrum_set_pause_ms( block, 0 ); libspectrum_tape_block_set_bits_in_last_byte( block, length % LIBSPECTRUM_BITS_IN_BYTE ? length % LIBSPECTRUM_BITS_IN_BYTE : LIBSPECTRUM_BITS_IN_BYTE ); data_length = tape_length / LIBSPECTRUM_BITS_IN_BYTE; libspectrum_tape_block_set_data_length( block, data_length ); tape_buffer = libspectrum_new0( libspectrum_byte, data_length ); libspectrum_byte *from = buffer; libspectrum_byte *to = tape_buffer; length = tape_length; do { libspectrum_byte val = 0; int i; for( i = 7; i >= 0; i-- ) { if( *from++ > 127 ) val |= 1 << i; } *to++ = val; } while ((length -= 8) > 0); libspectrum_tape_block_set_data( block, tape_buffer ); libspectrum_tape_append_block( tape, block ); if( afCloseFile( handle ) ) { libspectrum_free( buffer ); libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "libspectrum_wav_read: failed to close audio file" ); return LIBSPECTRUM_ERROR_UNKNOWN; } libspectrum_free( buffer ); /* Successful completion */ return LIBSPECTRUM_ERROR_NONE; }
/* Convert RLE block to a TZX DRB as TZX CSW block support is limited :/ */ static libspectrum_error tzx_write_rle( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length, libspectrum_tape *tape, libspectrum_tape_iterator iterator ) { libspectrum_error error; libspectrum_tape_block_state it; libspectrum_dword scale = libspectrum_tape_block_scale( block ); libspectrum_dword pulse_tstates = 0; libspectrum_dword balance_tstates = 0; int flags = 0; libspectrum_tape_block *raw_block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_RAW_DATA ); libspectrum_tape_block_set_bit_length( raw_block, scale ); libspectrum_tape_block_set_pause ( raw_block, 0 ); rle_state.bits_used = 0; rle_state.level = 0; rle_state.length = 0; rle_state.tape_length = 8192; rle_state.tape_buffer = libspectrum_malloc( rle_state.tape_length ); *rle_state.tape_buffer = 0; it.current_block = iterator; error = libspectrum_tape_block_init( block, &it ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( rle_state.tape_buffer ); libspectrum_tape_block_free( raw_block ); return error; } while( !(flags & LIBSPECTRUM_TAPE_FLAGS_BLOCK) ) { libspectrum_dword pulse_length = 0; /* Use internal version of this that doesn't bugger up the external tape status */ error = libspectrum_tape_get_next_edge_internal( &pulse_tstates, &flags, tape, &it ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( rle_state.tape_buffer ); libspectrum_tape_block_free( raw_block ); return error; } balance_tstates += pulse_tstates; /* next set of pulses is: balance_tstates / scale; */ pulse_length = balance_tstates / scale; balance_tstates = balance_tstates % scale; /* write pulse_length bits of the current level into the buffer */ write_pulse( pulse_length ); } if( rle_state.length || rle_state.bits_used ) { if( rle_state.bits_used ) { rle_state.length++; } else { rle_state.bits_used = 8; } error = libspectrum_tape_block_set_bits_in_last_byte( raw_block, rle_state.bits_used ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( rle_state.tape_buffer ); libspectrum_tape_block_free( raw_block ); return error; } error = libspectrum_tape_block_set_data_length( raw_block, rle_state.length ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( rle_state.tape_buffer ); libspectrum_tape_block_free( raw_block ); return error; } error = libspectrum_tape_block_set_data( raw_block, rle_state.tape_buffer ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( rle_state.tape_buffer ); libspectrum_tape_block_free( raw_block ); return error; } /* now have tzx_write_raw_data finish the job */ tzx_write_raw_data( raw_block, buffer, ptr, length ); } else { libspectrum_free( rle_state.tape_buffer ); } return libspectrum_tape_block_free( raw_block ); }