Important update: Arm Announces End of Life Timeline for Mbed. This site will be archived in July 2026. Read the full announcement.
Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
Timing of DAC
Philips Philips wrote:
Sorry total disagree, this is my favourite design based around a SAR you can't beat SAR converter in my eyes. I guess you never try a Flash ADC Gert, but for my projects SAR converter are the best to use:)
For DAC maybe the DSD1792 or PCM1792 is more what you are looking for, for ADC the ads8363 ?
Gert van der Knokke wrote:
Philips Philips wrote:
Sorry total disagree, this is my favourite design based around a SAR you can't beat SAR converter in my eyes. I guess you never try a Flash ADC Gert, but for my projects SAR converter are the best to use:)
For DAC maybe the DSD1792 or PCM1792 is more what you are looking for, for ADC the ads8363 ?
Nope OKi MSM7702 is good enough for me thanks... and so is lpc1768.. it just because my dsylexic brain did not pick up on that it was only a 10 bit dac and I am getting frustrated with the lack of help and the clicking.. and that I should not have being using it above a 10 bit resolution. And you all think I am super intelligent and should know everything like I am physic or something lol
And I myself really like Charles and Yeo designs and concepts theorys and I like reading yeo's blogs and Charles when ever he does a blog entry but Charles he don't really write much on blogs.
I think you was a bit harsh and that you have to at least respect the work that has gone into them both. I admire their work but I admire Panasonic recorder work more:) such as the Panasonic DR60 or QR100
Philips Philips wrote:
Nope OKi MSM7702 is good enough for me thanks... and so is lpc1768.. it just because my dsylexic brain did not pick up on that it was only a 10 bit dac and I am getting frustrated with the lack of help and the clicking.. and that I should not have being using it above a 10 bit resolution. And you all think I am super intelligent and should know everything like I am physic or something lol
Sorry, thought we were discussing high end audio, that OKI is for speech purpose only.
Quote:
And I myself really like Charles and Yeo designs and concepts theorys and I like reading yeo's blogs and Charles when ever he does a blog entry but Charles he don't really write much on blogs.
I think you was a bit harsh and that you have to at least respect the work that has gone into them both. I admire their work but I admire Panasonic recorder work more:) such as the Panasonic DR60 or QR100
As I said, it is no use on starting discussions on high-end audio :-)
To get a bit further on the clicking and such:
If you, for instance, try repeating a sample and the start and end do not match you will get clicking. You have to average the start and end samples to avoid this. The same will happen if you try and reverse samples: if both ends do not have the same level it will click. You can see this clearly on an oscilloscope (a memory type scope if available)
You could start with a simple waveform, say a sine wave divided into 360 steps. When you play a single wave (360 steps) in a loop both ends will be at zero level and it should produce a nice steady tone.
Now try and shorten the number of samples (say for instance: 270) You will have a maximum negative level at the end of your list of samples and then it suddenly jumps to zero at the beginning. Effect: you get a loud click each time the output goes from maximum negative to zero in almost zero time. Now take a few sample values from the end and from the beginning and average them towards eachother (add them up and divide them by two) and try the loop again. You will notice that it will sound much nicer.
You can understand that with 'random' waveforms (like realtime audio data) the sample values at the beginning and end are bound to be different so you need to average them to avoid 'sudden jumps'.
I hope this makes a bit of sense :-)
Gert van der Knokke wrote:
Philips Philips wrote:
Nope OKi MSM7702 is good enough for me thanks... and so is lpc1768.. it just because my dsylexic brain did not pick up on that it was only a 10 bit dac and I am getting frustrated with the lack of help and the clicking.. and that I should not have being using it above a 10 bit resolution. And you all think I am super intelligent and should know everything like I am physic or something lol
Sorry, thought we were discussing high end audio, that OKI is for speech purpose only.
Quote:
And I myself really like Charles and Yeo designs and concepts theorys and I like reading yeo's blogs and Charles when ever he does a blog entry but Charles he don't really write much on blogs.
I think you was a bit harsh and that you have to at least respect the work that has gone into them both. I admire their work but I admire Panasonic recorder work more:) such as the Panasonic DR60 or QR100
As I said, it is no use on starting discussions on high-end audio :-)
To get a bit further on the clicking and such:
If you, for instance, try repeating a sample and the start and end do not match you will get clicking. You have to average the start and end samples to avoid this. The same will happen if you try and reverse samples: if both ends do not have the same level it will click. You can see this clearly on an oscilloscope (a memory type scope if available)
You could start with a simple waveform, say a sine wave divided into 360 steps. When you play a single wave (360 steps) in a loop both ends will be at zero level and it should produce a nice steady tone.
Now try and shorten the number of samples (say for instance: 270) You will have a maximum negative level at the end of your list of samples and then it suddenly jumps to zero at the beginning. Effect: you get a loud click each time the output goes from maximum negative to zero in almost zero time. Now take a few sample values from the end and from the beginning and average them towards eachother (add them up and divide them by two) and try the loop again. You will notice that it will sound much nicer.
You can understand that with 'random' waveforms (like realtime audio data) the sample values at the beginning and end are bound to be different so you need to average them to avoid 'sudden jumps'.
I hope this makes a bit of sense :-)
I only get the clicking when I have added the reverse code, without the reverse code no clicking happens?
I think after I have change it to 8bit resolution it may fix the problem which I am trying to find out how to do
I have tried to change bSubFrameSize to 0x01 bBitResolution to 8 then WBVAL to 34 and I get bsod error on computer
/* Audio Type I Format */ AUDIO_FORMAT_TYPE_I_DESC_SZ(1), /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ AUDIO_FORMAT_TYPE_I, /* bFormatType */ 0x01, /* bNrChannels */ 0x02, /* bSubFrameSize */ 16, /* bBitResolution */ 0x01, /* bSamFreqType */ B3VAL(32000), /* tSamFreq */ /* Endpoint - Standard Descriptor */ AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(3), /* bEndpointAddress */ USB_ENDPOINT_TYPE_ISOCHRONOUS, /* bmAttributes */ WBVAL(64), /* wMaxPacketSize */ 0x01, /* bInterval */ 0x00, /* bRefresh */ 0x00, /* bSynchAddress */
/* Audio Definitions */ #define DATA_FREQ 32000 /* Audio Data Frequency */ #define P_S 32 /* Packet Size */ #if USB_DMA #define P_C 4 /* Packet Count */ #else #define P_C 1 /* Packet Count */ #endif #define B_S (8*P_C*P_S) /* Buffer Size */
I was told to do it like this 1 * 1 *(32 + 1) 34 and we made changes so any value could be enter instead of just number of power of 2 2,4,8,16,32,64
Well thats because when you are not reversing, the samples are 'continuous' from one to the next. (i.e sample 1 follows sample 2).
When you reverse, the one end of the buffer no longer follows from the other (at the boundary). You have no idea if your first sample is close to the last because you are turning the buffer around. One sample at the boundary could be 0 and the other could be 3.3V. When you reverse, these become next to each other resulting in a 'click'. This would be totally expected if you are not doing anything about it.
One suggestion was to average the two adjacent samples at the boundary. Or perhaps you can devise a way to flip your buffer at zero crossings (i.e. when both samples are equal)
I don't know to do it, just like you don't know how to do certain stuff:)
it's 10bit dac yet people have written code to run it a 16 bit resoultion you know
I keep telling you this
http://en.wikipedia.org/wiki/Dyslexia
here
Dyscalculia— a neurological condition characterized by a problem with basic sense of number and quantity and difficult retrieving rote math facts. Often people with this condition can understand very complex mathematical concepts and principles but have difficulty retrieving basic math facts involving addition and subtraction.
I do not know if visualization helps but here we go:
In this example you see a single sinewave (green) with an overlay of the middle part of that sinewave reversed (yellow) If I would play the resulting waveform the analog output would jump from maximum to minimum at about a quarter of the sample and again at threequarter of the sample and that would give a 'click' like this:
The red line marks the enormous jump the output has to make.
To eliminate the clicks you could try (as suggested) and find cutting points as close to zero as possible (easier) or average the values so they are smoothed out towards eachother (more difficult).
Gert van der Knokke wrote:
I do not know if visualization helps but here we go:
In this example you see a single sinewave (green) with an overlay of the middle part of that sinewave reversed (yellow) If I would play the resulting waveform the analog output would jump from maximum to minimum at about a quarter of the sample and again at threequarter of the sample and that would give a 'click' like this:
The red line marks the enormous jump the output has to make.
To eliminate the clicks you could try (as suggested) and find cutting points as close to zero as possible (easier) or average the values so they are smoothed out towards eachother (more difficult).
If you wrote a bit of example code and gave me the complex mathematics would help more be better:) I need to see code mathematics not words.
Maybe something like this might work:
Take 10 sample values at the beginning of the reversed part and 10 sample values at the end and fade them in/out toward zero.
// buffer for our reversed samples (I have used signed int as values) int samples[MAX_SAMPLES]; // the multiplier we are going to apply to the sample value float multiplier=0.0; for (t=0; t<10; t++){ // fade out end samples to zero sample[MAX_SAMPLES-t]=sample[MAX_SAMPLES-t]*multiplier; // fade in start samples from zero sample[t]=sample[t]*multiplier; // increment multiplier so we end up at 1 after 10 samples multiplier=multiplier+0.1; }
This way your reversed sample is 'softened' at the start and end. If the reversed sample is in the middle of a longer (normal) sample you might need to soften the end of the normal part where the reversed part starts and the start of where the normal part continues too. Sorry, it IS complex matter :-)
Ok thanks this is current code, it will take me a while to try to figure out how I am supposed to fit this in to my code
// Hello World example for the USBMIDI library (circular buffer) #include "mbed.h" #include "USBAudio.h" #include "CircBuffer.h" Serial pc(USBTX, USBRX); // frequency: 48 kHz #define FREQ 32000 // 1 channel: mono #define NB_CHA 1 // length computed: each ms, we receive 48 * 16bits ->48 * 2 bytes. as there is one channel, the length will be 48 * 2 * 1 #define LENGTH_AUDIO_PACKET 32 * 2 * 1 #define USR_POWERDOWN (0x104) int semihost_powerdown() { uint32_t arg; return __semihost(USR_POWERDOWN, &arg); } // USBAudio USBAudio USBaudio(FREQ, NB_CHA, 0x7186, 0x7507); // speaker connected to the AnalogOut output. The audio stream received over USb will be sent to the speaker AnalogOut speaker(p18); // ticker to send data to the speaker at the good frequency Ticker tic; // circular buffer where will be stored data to be sent to the speaker CircBuffer<int16_t> cbuf(LENGTH_AUDIO_PACKET); // previous value sent to the speaker uint16_t p_val = 0; // function executed each 1/FREQ s void tic_handler() { int16_t val; float speaker_value; unsigned short PlaySample; if (cbuf.available() >= 1) { cbuf.dequeue(&val); speaker_value = (float)val; // speaker_value between 0 and 65535 speaker_value += 32768.0; // adjust according to current volume speaker_value *= USBaudio.getVolume(); } else { speaker_value = p_val; } p_val = speaker_value; /* ADAMGR: Inserting my May 30th reversing code here!!! */ { static const unsigned int BufferSize = 20 * 640; static unsigned short Buffer[BufferSize]; static int Index = 0; static int Direction = 1; static int Playback = false; static int ChunkSize = BufferSize; unsigned short ReadSample; /* Default PlaySample to the current sample from USB buffer. */ PlaySample = speaker_value; /* Read out the sample from the buffer to be played back */ if (Playback) { PlaySample = Buffer[Index]; } /* Obtain current audio sample from the USB buffer. */ ReadSample = (unsigned short)speaker_value; /* Record the sample into the buffer right where a space was freed up from the PlaySample read above */ Buffer[Index] = ReadSample; /* Increment the buffer pointer */ Index += Direction; /* Check to see if the chunk has been filled */ if (Index < 0) { /* Now have a chunk to be played back */ Playback = true; /* Reverse the direction of playback and recording */ Direction *= -1;//Direction; Index = 0; } else if (Index >= ChunkSize) { /* Now have a chunk to be played back */ Playback = true; /* Reverse the direction of playback and recording */ Direction *= -1;//Direction; Index = ChunkSize - 1; } } // send value to the speaker speaker.write_u16((uint16_t)PlaySample); } int main() { int16_t buf[LENGTH_AUDIO_PACKET/2]; semihost_powerdown(); // attach a function executed each 1/FREQ s tic.attach_us(tic_handler, 1000000.0/(float)FREQ); while (1) { // read an audio packet USBaudio.read((uint8_t *)buf); // put buffer in the circ buffer for(int i = 0; i < LENGTH_AUDIO_PACKET/2; i++) { cbuf.queue(buf[i]); } } }
I have thought for some time that I just need to take the reversing code out of the timer 0 the Ticker tic..and run a second slower timer or ticker or just have it in the main loop, but I did not know how to transfer the buffer out of the timer routine or if it was possible.
Well I had a bash don't know if this is how you thought it may have been done it gert
// Hello World example for the USBMIDI library (circular buffer) #include "mbed.h" #include "USBAudio.h" #include "CircBuffer.h" Serial pc(USBTX, USBRX); // frequency: 48 kHz #define FREQ 32000 // 1 channel: mono #define NB_CHA 1 // length computed: each ms, we receive 48 * 16bits ->48 * 2 bytes. as there is one channel, the length will be 48 * 2 * 1 #define LENGTH_AUDIO_PACKET 32 * 2 * 1 #define USR_POWERDOWN (0x104) int semihost_powerdown() { uint32_t arg; return __semihost(USR_POWERDOWN, &arg); } // USBAudio USBAudio USBaudio(FREQ, NB_CHA, 0x7186, 0x7507); // speaker connected to the AnalogOut output. The audio stream received over USb will be sent to the speaker AnalogOut speaker(p18); // ticker to send data to the speaker at the good frequency Ticker tic; // circular buffer where will be stored data to be sent to the speaker CircBuffer<int16_t> cbuf(LENGTH_AUDIO_PACKET); // previous value sent to the speaker uint16_t p_val = 0; // function executed each 1/FREQ s void tic_handler() { int16_t val; float speaker_value; unsigned short PlaySample; if (cbuf.available() >= 1) { cbuf.dequeue(&val); speaker_value = (float)val; // speaker_value between 0 and 65535 speaker_value += 32768.0; // adjust according to current volume speaker_value *= USBaudio.getVolume(); } else { speaker_value = p_val; } p_val = speaker_value; /* ADAMGR: Inserting my May 30th reversing code here!!! */ { static const unsigned int BufferSize = 5 * 1024; static unsigned short Buffer[BufferSize]; // int samples[MAX_SAMPLES]; static int Index = 0; static int Direction = 1; static int Playback = false; static int ChunkSize = BufferSize; // float multiplier = 0.0; unsigned short ReadSample; /* Default PlaySample to the current sample from USB buffer. */ PlaySample = speaker_value; /* Read out the sample from the buffer to be played back */ if (Playback) { PlaySample = Buffer[Index]; } /* Obtain current audio sample from the USB buffer. */ ReadSample = (unsigned short)speaker_value; /* Record the sample into the buffer right where a space was freed up from the PlaySample read above */ Buffer[Index] = ReadSample; /* Increment the buffer pointer */ Index += Direction; /* Check to see if the chunk has been filled */ if (Index < 0) { /* Now have a chunk to be played back */ Playback = true; /* Reverse the direction of playback and recording */ Direction *= -1;//Direction; Index = 0; } else if (Index >= ChunkSize) { /* Now have a chunk to be played back */ Playback = true; /* Reverse the direction of playback and recording */ Direction *= -1;//Direction; Index = ChunkSize - 1; int t = 0; // buffer for our reversed samples (I have used signed int as values) int MAX_SAMPLES = 3 * 1024; int sample[MAX_SAMPLES]; // the multiplier we are going to apply to the sample value float multiplier=0.0; MAX_SAMPLES = Index; for (t=0; t<10; t++){ // fade out end samples to zero sample[MAX_SAMPLES-t]=sample[MAX_SAMPLES-t]*multiplier; // fade in start samples from zero sample[t]=sample[t]*multiplier; // increment multiplier so we end up at 1 after 10 samples multiplier=multiplier+0.1; } } // send value to the speaker speaker.write_u16((uint16_t)PlaySample); } } int main() { int16_t buf[LENGTH_AUDIO_PACKET/2]; semihost_powerdown(); // attach a function executed each 1/FREQ s tic.attach_us(tic_handler, 1000000.0/(float)FREQ); while (1) { // read an audio packet USBaudio.read((uint8_t *)buf); // put buffer in the circ buffer for(int i = 0; i < LENGTH_AUDIO_PACKET/2; i++) { cbuf.queue(buf[i]); } } }
I have this reversing code as well it is complexed tho from http://trac.metadecks.org/browser/sweep/trunk one of my fav audio editors:)
/* 2 * Sweep, a sound wave editor. 3 * 4 * Copyright (C) 2000 Conrad Parker 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 */ 20 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <gdk/gdkkeysyms.h> 25 26 #include <sweep/sweep.h> 27 28 #include "../src/sweep_app.h" /* XXX */ 29 30 static sw_sample * 31 sounddata_reverse (sw_sample * sample, sw_param_set pset, 32 gpointer custom_data) 33 { 34 GList * gl; 35 sw_sel * sel; 36 glong i, sw; 37 sw_sounddata * sounddata; 38 sw_format * format; 39 sw_framecount_t nr_frames; 40 gpointer d, e, t; 41 42 sw_framecount_t op_total, run_total; 43 sw_framecount_t remaining, n; 44 45 gboolean active = TRUE; 46 47 sounddata = sample_get_sounddata (sample); 48 format = sounddata->format; 49 50 op_total = sounddata_selection_nr_frames (sounddata) / 200; 51 if (op_total == 0) op_total = 1; 52 run_total = 0; 53 54 sw = frames_to_bytes (format, 1); 55 t = alloca (sw); 56 57 for (gl = sounddata->sels; active && gl; gl = gl->next) { 58 sel = (sw_sel *)gl->data; 59 60 d = sounddata->data + frames_to_bytes (format, sel->sel_start); 61 nr_frames = sel->sel_end - sel->sel_start; 62 63 e = d + frames_to_bytes (format, nr_frames); 64 65 remaining = nr_frames/2; 66 67 while (active && remaining > 0) { 68 g_mutex_lock (sample->ops_mutex); 69 70 if (sample->edit_state == SWEEP_EDIT_STATE_CANCEL) { 71 active = FALSE; 72 } else { 73 n = MIN (remaining, 1024); 74 75 for (i = 0; i <= n; i++) { 76 memcpy (t, d, sw); 77 memcpy (d, e, sw); 78 memcpy (e, t, sw); 79 80 d += sw; 81 e -= sw; 82 } 83 84 remaining -= n; 85 86 run_total += n; 87 sample_set_progress_percent (sample, run_total / op_total); 88 } 89 90 g_mutex_unlock (sample->ops_mutex); 91 } 92 } 93 94 return sample; 95 } 96 97 static sw_op_instance * 98 apply_reverse (sw_sample * sample, sw_param_set pset, gpointer custom_data) 99 { 100 return 101 perform_filter_op (sample, _("Reverse"), 102 (SweepFilter)sounddata_reverse, pset, NULL); 103 } 104 105 106 static sw_procedure proc_reverse = { 107 N_("Reverse"), 108 N_("Reverse selected regions of a sample"), 109 "Conrad Parker", 110 "Copyright (C) 2000", 111 "http://sweep.sourceforge.net/plugins/reverse", 112 "Filters/Reverse", /* identifier */ 113 GDK_f, /* accel_key */ 114 GDK_SHIFT_MASK, /* accel_mods */ 115 0, /* nr_params */ 116 NULL, /* param_specs */ 117 NULL, /* suggests() */ 118 apply_reverse, 119 NULL, /* custom_data */ 120 }; 121 122 static GList * 123 reverse_init (void) 124 { 125 return g_list_append ((GList *)NULL, &proc_reverse); 126 } 127 128 129 sw_plugin plugin = { 130 reverse_init, /* plugin_init */ 131 NULL, /* plugin_cleanup */ 132 };
complexed http://trac.metadecks.org/browser/sweep/trunk/src/play.c
The reversing is not the problem :-)
From the source I can recognize the following code for swapping 'frames' (that is multiple samples, often just left and right but with multiple audiotracks it can be more than that)
77 for (i = 0; i <= n; i++) { // number of frames to swap / 2 76 memcpy (t, d, sw); // save frame to be overwritten in a temp space 77 memcpy (d, e, sw); // copy start to end 78 memcpy (e, t, sw); // copy end to start 79 80 d += sw; // increment start pointer 81 e -= sw; // decrement end pointer 82 } 83
So this is not much different except for the more flexible frame size instead of a fixed sample size.
If you do this somewhere inside the middle of a longer sample it will click just as bad as your own code...
As for your earlier message:
The softening needs only to be done once after you have received an audio packet from the USB.
So the order of functions is:
while(1){ Wait for an audio packet being received Reverse the buffer Soften the buffer Playback buffer }
Gert van der Knokke wrote:
The reversing is not the problem :-)
From the source I can recognize the following code for swapping 'frames' (that is multiple samples, often just left and right but with multiple audiotracks it can be more than that)
77 for (i = 0; i <= n; i++) { // number of frames to swap / 2 76 memcpy (t, d, sw); // save frame to be overwritten in a temp space 77 memcpy (d, e, sw); // copy start to end 78 memcpy (e, t, sw); // copy end to start 79 80 d += sw; // increment start pointer 81 e -= sw; // decrement end pointer 82 } 83
So this is not much different except for the more flexible frame size instead of a fixed sample size.
If you do this somewhere inside the middle of a longer sample it will click just as bad as your own code...
As for your earlier message:
The softening needs only to be done once after you have received an audio packet from the USB.
So the order of functions is:
while(1){ Wait for an audio packet being received Reverse the buffer Soften the buffer Playback buffer }
Gert you could have taken the time to browse the trunk for sweep audio editor the code above is not meant for streaming audio, the code is for highlighting a peace of audio and then reversing it, that means after you have recorded the audio and nothing is moving you highlight and press the reverse key from the sweep audio edit.
Sweep audio editor is really complexed I did not post it the play.c file because it's so big that is why I just put the link at the bottom, but you just ignored it.
Please take the time to realized what I posted when I posted sweep, it let you scrub as in mix audio live audio forward or reverse with us or ms delay OK:)
http://www.metadecks.org/software/sweep/
Sweep, a sound wave editor. 3 * 4 * Copyright (C) 2000 Conrad Parker 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 */ 20 21 #ifdef HAVE_CONFIG_H 22 # include <config.h> 23 #endif 24 25 /* Define this to record all output to files in /tmp */ 26 /* #define RECORD_DEMO_FILES */ 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <sys/time.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <unistd.h> 35 #include <fcntl.h> 36 #include <math.h> 37 #include <sys/ioctl.h> 38 #include <pthread.h> 39 40 #ifdef RECORD_DEMO_FILES 41 #include <sndfile.h> 42 43 #if !defined (SNDFILE_1) 44 #error Recording demo files requires libsndfile version 1 45 #endif 46 47 #endif 48 49 #include <sweep/sweep_types.h> 50 #include <sweep/sweep_sample.h> 51 #include <sweep/sweep_typeconvert.h> 52 53 #include "play.h" 54 #include "head.h" 55 #include "driver.h" 56 #include "preferences.h" 57 #include "sample-display.h" 58 59 /*#define DEBUG*/ 60 61 #define SCRUB_SLACKNESS 2.0 62 63 #define USE_MONITOR_KEY "UseMonitor" 64 65 static GMutex * play_mutex = NULL; 66 67 static sw_handle * main_handle = NULL; 68 static sw_handle * monitor_handle = NULL; 69 70 static GList * active_main_heads = NULL; 71 static GList * active_monitor_heads = NULL; 72 73 /*static int realoffset = 0;*/ 74 static sw_sample * prev_sample = NULL; 75 static pthread_t player_thread = (pthread_t)-1; 76 77 static gboolean stop_all = FALSE; 78 79 static sw_audio_t * pbuf = NULL, * devbuf = NULL; 80 static int pbuf_chans = 0, devbuf_chans = 0; 81 82 83 /* 84 * update_playmarker () 85 * 86 * Update the position of the playback marker line for the sample 87 * being played. 88 * 89 * Called within the main sweep interface thread. 90 * gtk_idle will keep calling this function as long as this sample is 91 * playing, unless otherwise stopped. 92 */ 93 static gint 94 update_playmarker (gpointer data) 95 { 96 sw_sample * s = (sw_sample *)data; 97 sw_head * head = s->play_head; 98 99 if (!sample_bank_contains (s)) { 100 return FALSE; 101 } else if (!head->going) { 102 103 #ifdef DEBUG 104 g_print ("update_playmarker: !head->going\n"); 105 #endif 106 s->playmarker_tag = 0; 107 108 /* Set user offset to correct offset */ 109 if (head->previewing) { 110 sample_set_playmarker (s, head->stop_offset, TRUE); 111 head_set_previewing (head, FALSE); 112 } else { 113 sample_set_playmarker (s, (sw_framecount_t)head->offset, TRUE); 114 } 115 116 /* As this may have been stopped by the player thread, refresh the 117 * interface */ 118 head_set_going (head, FALSE); 119 sample_refresh_playmode (s); 120 121 return FALSE; 122 } else { 123 sample_set_playmarker (s, head->realoffset, FALSE); 124 125 return TRUE; 126 } 127 } 128 129 static void 130 start_playmarker (sw_sample * s) 131 { 132 if (s->playmarker_tag > 0) { 133 g_source_remove (s->playmarker_tag); 134 } 135 136 s->playmarker_tag = 137 g_timeout_add ((guint32)30, 138 (GSourceFunc) update_playmarker, 139 (gpointer)s); 140 } 141 142 static sw_framecount_t 143 head_read_unrestricted (sw_head * head, sw_audio_t * buf, 144 sw_framecount_t count, int driver_rate) 145 { 146 sw_sample * sample = head->sample; 147 sw_sounddata * sounddata = sample->sounddata; 148 sw_format * f = sounddata->format; 149 sw_audio_t * d; 150 gdouble po = 0.0, p; 151 gfloat relpitch; 152 sw_framecount_t i, j, b; 153 gint si=0, si_next = 0; 154 gboolean interpolate = FALSE; 155 gboolean do_smoothing = FALSE; 156 sw_framecount_t last_user_offset = -1; 157 int pbuf_size = count * f->channels; 158 gdouble scrub_rate = f->rate / 30.0; 159 160 d = (sw_audio_t *)sounddata->data; 161 162 b = 0; 163 164 po = head->offset; 165 166 /* compensate for sampling rate of driver */ 167 relpitch = (gfloat)((gdouble)f->rate / (gdouble)driver_rate); 168 169 for (i = 0; i < count; i++) { 170 if (head->mute || sample->user_offset == last_user_offset) { 171 for (j = 0; j < f->channels; j++) { 172 buf[b] = 0.0; 173 b++; 174 } 175 } else { 176 si = (int)floor(po); 177 178 interpolate = (si < sounddata->nr_frames); 179 180 p = po - (gdouble)si; 181 si *= f->channels; 182 g_mutex_lock(head->sample->sounddata->data_mutex); 183 184 if (interpolate) { 185 si_next = si+f->channels; 186 for (j = 0; j < f->channels; j++) { 187 buf[b] = head->gain * (((sw_audio_t *)head->sample->sounddata->data)[si] * p + ((sw_audio_t *)head->sample->sounddata->data)[si_next] * (1 - p)); 188 if (do_smoothing) { 189 sw_framecount_t b1, b2; 190 b1 = (b - f->channels + pbuf_size) % pbuf_size; 191 b2 = (b1 - f->channels + pbuf_size) % pbuf_size; 192 buf[b] += buf[b] * 2.0; 193 buf[b] += buf[b1] * 3.0 + buf[b2] * 4.0; 194 buf[b] /= 10.0; 195 } 196 b++; si++; si_next++; 197 } 198 } else { 199 for (j = 0; j < f->channels; j++) { 200 buf[b] = head->gain * ((sw_audio_t *)head->sample->sounddata->data)[si]; 201 if (do_smoothing) { 202 sw_framecount_t b1, b2; 203 b1 = (b - f->channels + pbuf_size) % pbuf_size; 204 b2 = (b1 - f->channels + pbuf_size) % pbuf_size; 205 buf[b] += buf[b] * 2.0; 206 buf[b] += buf[b1] * 3.0 + buf[b2] * 4.0; 207 buf[b] /= 10.0; 208 } 209 b++; si++; 210 } 211 } 212 g_mutex_unlock(head->sample->sounddata->data_mutex); 213 } 214 215 if (head->scrubbing) { 216 gfloat new_delta; 217 218 if (sample->by_user) { 219 new_delta = (sample->user_offset - po) / scrub_rate; 220 221 head->delta = head->delta * 0.9 + new_delta * 0.1; 222 223 sample->by_user = FALSE; 224 225 last_user_offset = sample->user_offset; 226 } else { 227 gfloat new_po, u_po = (gdouble)sample->user_offset; 228 229 new_delta = (sample->user_offset - po) / scrub_rate; 230 231 head->delta = head->delta * 0.99 + new_delta * 0.01; 232 233 new_po = po + (head->delta * relpitch); 234 235 if ((head->delta < 0 && new_po < u_po) || 236 (head->delta > 0 && new_po > u_po)) { 237 po = u_po; 238 head->delta = 0.0; 239 } 240 } 241 242 do_smoothing = TRUE; 243 244 } else { 245 gfloat tdelta = head->rate * sample->rate; 246 gfloat hdelta = head->delta * (head->reverse ? -1.0 : 1.0); 247 248 if (sample->by_user) { 249 head->offset = (gdouble)sample->user_offset; 250 po = head->offset; 251 head->delta = tdelta; 252 253 sample->by_user = FALSE; 254 do_smoothing = TRUE; 255 256 last_user_offset = sample->user_offset; 257 } 258 259 if (hdelta < -0.3 * tdelta || hdelta > 1.001 * tdelta) { 260 head->delta *= 0.9999; 261 } else if (hdelta < 0.7 * tdelta) { 262 head->delta = 0.8 * tdelta * (head->reverse ? -1.0 : 1.0); 263 } else if (hdelta < .999 * tdelta) { 264 head->delta *= 1.0001; 265 } else { 266 head->delta = tdelta * (head->reverse ? -1.0 : 1.0); 267 } 268 269 do_smoothing = FALSE; 270 } 271 272 po += head->delta * relpitch; 273 274 { 275 gdouble nr_frames = (gdouble)sample->sounddata->nr_frames; 276 if (head->looping) { 277 while (po < 0.0) po += nr_frames; 278 while (po > nr_frames) po -= nr_frames; 279 } else { 280 if (po < 0.0) po = 0.0; 281 else if (po > nr_frames) po = nr_frames; 282 } 283 } 284 285 head->offset = po; 286 287 } 288 289 return count; 290 } 291 292 sw_framecount_t 293 head_read (sw_head * head, sw_audio_t * buf, sw_framecount_t count, 294 int driver_rate) 295 { 296 sw_sample * sample = head->sample; 297 sw_sounddata * sounddata = sample->sounddata; 298 sw_format * f = sounddata->format; 299 sw_framecount_t head_offset; 300 sw_framecount_t remaining = count, written = 0, n = 0; 301 sw_framecount_t delta, bound; 302 GList * gl; 303 sw_sel * sel, * osel; 304 305 while (head->going && remaining > 0) { 306 n = 0; 307 308 if (head->restricted /* && !head->scrubbing */) { 309 g_mutex_lock (sounddata->sels_mutex); 310 311 if (g_list_length (sounddata->sels) == 0) { 312 g_mutex_unlock (sounddata->sels_mutex); 313 314 if (head->previewing && !head->scrubbing) { 315 if (head->reverse) { 316 n = MIN (remaining, head->offset - head->stop_offset); 317 } else { 318 n = MIN (remaining, head->stop_offset - head->offset); 319 } 320 goto got_n; 321 322 } else { 323 goto zero_pad; 324 } 325 } 326 327 if (head->previewing && !head->scrubbing) { 328 /* Find selection region that offset is or should be playing to */ 329 if (head->reverse) { 330 osel = NULL; 331 for (gl = g_list_last (sounddata->sels); gl; gl = gl->prev) { 332 sel = (sw_sel *)gl->data; 333 334 if (osel && ((sw_framecount_t)head->offset > osel->sel_start)) 335 head->offset = osel->sel_start; 336 337 osel = sel; 338 339 head_offset = (sw_framecount_t)head->offset; 340 341 if (head_offset > sel->sel_end) { 342 n = MIN (remaining, head_offset - sel->sel_end); 343 break; 344 } 345 } 346 347 /* If now at start of first selection region ... */ 348 if (gl == NULL && osel != NULL) { 349 if ((sw_framecount_t)head->offset > osel->sel_start) 350 head->offset = osel->sel_start; 351 352 head_offset = (sw_framecount_t)head->offset; 353 354 /* continue 1 second */ 355 delta = time_to_frames (sounddata->format, 1.0); 356 bound = MAX((osel->sel_start - delta), 0); 357 if (head_offset > bound) { 358 n = MIN (remaining, head_offset - bound); 359 } 360 } 361 362 } else { 363 osel = NULL; 364 for (gl = sounddata->sels; gl; gl = gl->next) { 365 sel = (sw_sel *)gl->data; 366 367 if (osel && ((sw_framecount_t)head->offset < osel->sel_end)) 368 head->offset = osel->sel_end; 369 370 osel = sel; 371 372 head_offset = (sw_framecount_t)head->offset; 373 374 if (head_offset < sel->sel_start) { 375 n = MIN (remaining, sel->sel_start - head_offset); 376 break; 377 } 378 } 379 380 /* If now at end of last selection region ... */ 381 if (gl == NULL && osel != NULL) { 382 if ((sw_framecount_t)head->offset < osel->sel_end) 383 head->offset = osel->sel_end; 384 385 head_offset = head->offset; 386 387 /* continue 1 second */ 388 delta = time_to_frames (sounddata->format, 1.0); 389 bound = MIN ((osel->sel_end + delta), sounddata->nr_frames); 390 if (head_offset < bound) { 391 n = MIN (remaining, bound - head_offset); 392 } 393 } 394 } 395 } else { 396 /* Find selection region that offset is or should be in */ 397 if (head->reverse) { 398 for (gl = g_list_last (sounddata->sels); gl; gl = gl->prev) { 399 sel = (sw_sel *)gl->data; 400 401 if ((sw_framecount_t)head->offset > sel->sel_end) 402 head->offset = sel->sel_end; 403 404 if ((sw_framecount_t)head->offset > sel->sel_start) { 405 n = MIN (remaining, (sw_framecount_t)head->offset - sel->sel_start); 406 break; 407 } 408 } 409 } else { 410 for (gl = sounddata->sels; gl; gl = gl->next) { 411 sel = (sw_sel *)gl->data; 412 413 if ((sw_framecount_t)head->offset < sel->sel_start) 414 head->offset = sel->sel_start; 415 416 if ((sw_framecount_t)head->offset < sel->sel_end) { 417 n = MIN (remaining, sel->sel_end - (sw_framecount_t)head->offset); 418 break; 419 } 420 } 421 } 422 } 423 424 g_mutex_unlock (sounddata->sels_mutex); 425 426 } else { /* unrestricted */ 427 if (head->previewing && !head->scrubbing) { 428 if (head->reverse) { 429 n = MIN (remaining, (sw_framecount_t)head->offset - head->stop_offset); 430 } else { 431 n = MIN (remaining, head->stop_offset - (sw_framecount_t)head->offset); 432 } 433 } else { 434 if (head->reverse) { 435 n = MIN (remaining, (sw_framecount_t)head->offset); 436 } else { 437 n = MIN (remaining, sounddata->nr_frames - (sw_framecount_t)head->offset); 438 } 439 } 440 } 441 442 got_n: 443 444 if (n == 0) { 445 if (head->previewing) { 446 head->offset = head->stop_offset; 447 } else if (!head->restricted || sounddata->sels == NULL) { 448 head->offset = head->reverse ? sounddata->nr_frames : 0; 449 } else { 450 g_mutex_lock (sounddata->sels_mutex); 451 if (head->reverse) { 452 gl = g_list_last (sounddata->sels); 453 sel = (sw_sel *)gl->data; 454 head->offset = sel->sel_end; 455 } else { 456 gl = sounddata->sels; 457 sel = (sw_sel *)gl->data; 458 head->offset = sel->sel_start; 459 } 460 g_mutex_unlock (sounddata->sels_mutex); 461 } 462 463 if (!head->looping || head->previewing) { 464 head->going = FALSE; 465 } 466 } else { 467 if (n < 0) { 468 #ifdef DEBUG 469 printf ("n = %d\n", n); 470 #endif 471 head->going = FALSE; 472 #ifdef DEUBG 473 } else if (n > count) { 474 printf ("n = %d \t>\tcount = %d\n", n, count); 475 #endif 476 } else { 477 written += head_read_unrestricted (head, buf, n, driver_rate); 478 buf += (int)frames_to_samples (f, n); 479 remaining -= n; 480 } 481 } 482 } 483 484 zero_pad: 485 486 if (remaining > 0) { 487 n = frames_to_bytes (f, remaining); 488 memset (buf, 0, n); 489 written += remaining; 490 } 491 492 return written; 493 } 494 495 /* initialise a head for playback */ 496 static void 497 head_init_playback (sw_sample * s) 498 { 499 sw_head * head = s->play_head; 500 GList * gl; 501 sw_sel * sel; 502 sw_framecount_t sels_start, sels_end; 503 sw_framecount_t delta; 504 505 /* g_mutex_lock (s->play_mutex);*/ 506 507 s->by_user = FALSE; 508 509 if (!head->going) { 510 head_set_offset (head, s->user_offset); 511 head->delta = head->rate * s->rate; 512 } 513 514 515 if (head->restricted) { 516 g_mutex_lock (s->sounddata->sels_mutex); 517 518 if ((gl = s->sounddata->sels) != NULL) { 519 sel = (sw_sel *)gl->data; 520 sels_start = sel->sel_start; 521 522 gl = g_list_last (s->sounddata->sels); 523 524 sel = (sw_sel *)gl->data; 525 sels_end = sel->sel_end; 526 g_mutex_unlock (s->sounddata->sels_mutex); 527 528 if (head->previewing) { 529 /* preroll 1 second */ 530 delta = time_to_frames (s->sounddata->format, 1.0); 531 532 if (head->reverse) { 533 head_set_offset (head, sels_end + delta); 534 } else { 535 head_set_offset (head, sels_start - delta); 536 } 537 538 head_set_offset (head, 539 CLAMP((sw_framecount_t)head->offset, 0, s->sounddata->nr_frames)); 540 } else { 541 if (head->reverse && head->offset <= sels_start) { 542 head_set_offset (head, sels_end); 543 } else if (!head->reverse && head->offset >= sels_end) { 544 head_set_offset (head, sels_start); 545 } 546 } 547 } else if (head->previewing) { 548 /* preroll 1 second */ 549 delta = time_to_frames (s->sounddata->format, 1.0); 550 551 if (head->reverse) { 552 head_set_offset (head, head->offset + delta); 553 } else { 554 head_set_offset (head, head->offset - delta); 555 } 556 557 head_set_offset (head, 558 CLAMP(head->offset, 0, s->sounddata->nr_frames)); 559 } 560 } else { 561 562 if (head->previewing) { 563 /* preroll 1 second */ 564 delta = time_to_frames (s->sounddata->format, 1.0); 565 if (head->reverse) head_set_offset (head, head->offset + delta); 566 else head_set_offset (head, head->offset - delta); 567 } 568 569 if (head->reverse && head->offset <= 0) { 570 head_set_offset (head, s->sounddata->nr_frames); 571 } else if (!head->reverse && head->offset >= s->sounddata->nr_frames) { 572 head_set_offset (head, 0); 573 } 574 } 575 576 /* g_mutex_unlock (s->play_mutex);*/ 577 } 578 579 static void 580 channel_convert_adding (sw_audio_t * src, int src_channels, 581 sw_audio_t * dest, int dest_channels, 582 sw_framecount_t n) 583 { 584 int j; 585 sw_framecount_t i, b = 0; 586 sw_audio_intermediate_t a; 587 588 if (src_channels == 1) { /* mix mono data up */ 589 for (i = 0; i < n; i++) { 590 for (j = 0; j < dest_channels; j++) { 591 dest[b] += src[i]; 592 b++; 593 } 594 } 595 } else if (dest_channels == 1) { /* mix down to mono */ 596 for (i = 0; i < n; i++) { 597 a = 0.0; 598 for (j = 0; j < src_channels; j++) { 599 a += src[b]; 600 b++; 601 } 602 a /= (sw_audio_intermediate_t)src_channels; 603 dest[i] += (sw_audio_t)a; 604 } 605 } else if (src_channels < dest_channels) { /* copy to first channels */ 606 for (i = 0; i < n; i++) { 607 for (j = 0; j < src_channels; j++) { 608 dest[i * dest_channels + j] += src[b]; 609 b++; 610 } 611 } 612 } else if (dest_channels < src_channels) { /* copy first channels only */ 613 for (i = 0; i < n; i++) { 614 for (j = 0; j < dest_channels; j++) { 615 dest[b] += src[i * src_channels + j]; 616 b++; 617 } 618 } 619 } else if (src_channels == dest_channels) { /* just add */ 620 for (i = 0; i < n * src_channels; i ++) { 621 dest[i] += src[i]; 622 } 623 } 624 } 625 626 static void 627 play_head_update_device (sw_head * head) 628 { 629 g_mutex_lock (play_mutex); 630 g_mutex_lock (head->head_mutex); 631 632 if (head->monitor) { 633 if (g_list_find (active_monitor_heads, head) == 0) { 634 active_monitor_heads = g_list_append (active_monitor_heads, head); 635 } 636 active_main_heads = g_list_remove (active_main_heads, head); 637 } else { 638 if (g_list_find (active_main_heads, head) == 0) { 639 active_main_heads = g_list_append (active_main_heads, head); 640 } 641 active_monitor_heads = g_list_remove (active_monitor_heads, head); 642 } 643 644 g_mutex_unlock (head->head_mutex); 645 g_mutex_unlock (play_mutex); 646 } 647 648 #ifdef RECORD_DEMO_FILES 649 static gchar * 650 generate_demo_filename (void) 651 { 652 return g_strdup_printf ("/tmp/sweep-demo-%d.au", getpid ()); 653 } 654 #endif 655 656 #define PSIZ 64 657 658 static void 659 prepare_to_play_heads (GList * heads, sw_handle * handle) 660 { 661 sw_head * head; 662 sw_format * f; 663 664 GList * gl; 665 666 if ((gl = heads) != NULL) { 667 head = (sw_head *)gl->data; 668 669 f = head->sample->sounddata->format; 670 671 if (f->channels > pbuf_chans) { 672 pbuf = g_realloc (pbuf, PSIZ * f->channels * sizeof (sw_audio_t)); 673 pbuf_chans = f->channels; 674 } 675 } 676 677 device_wait (handle); 678 679 return; 680 } 681 682 static void 683 play_heads (GList ** heads, sw_handle * handle) 684 { 685 sw_sample * s; 686 sw_head * head; 687 sw_format * f; 688 sw_framecount_t n; 689 690 GList * gl, * gl_next; 691 692 if (*heads == NULL) return; 693 694 n = PSIZ; 695 696 for (gl = *heads; gl; gl = gl_next) { 697 698 g_mutex_lock (play_mutex); 699 head = (sw_head *)gl->data; 700 gl_next = gl->next; 701 g_mutex_unlock (play_mutex); 702 703 if (!head) { 704 /* XXX: wtf??? */ 705 return; 706 } else if (!head->going || !sample_bank_contains (head->sample)) { 707 g_mutex_lock (play_mutex); 708 *heads = g_list_remove (*heads, head); 709 g_mutex_unlock (play_mutex); 710 } else { 711 s = head->sample; 712 f = s->sounddata->format; 713 714 if (f->channels > pbuf_chans) { 715 pbuf = g_realloc (pbuf, n * f->channels * sizeof (sw_audio_t)); 716 pbuf_chans = f->channels; 717 } 718 719 head_read (head, pbuf, n, handle->driver_rate); 720 721 channel_convert_adding (pbuf, f->channels, devbuf, 722 handle->driver_channels, n); 723 724 /* XXX: store the head->offset NOW for device_offset referencing */ 725 726 g_mutex_lock (s->play_mutex); 727 728 head->realoffset = device_offset (handle); 729 if (head->realoffset == -1) { 730 head->realoffset = head->offset; 731 } 732 733 head->offset = head->realoffset; 734 735 if (s->by_user /* && s->play_scrubbing */) { 736 /*head->offset = s->user_offset;*/ 737 } else { 738 if (!head->scrubbing) s->user_offset = head->realoffset; 739 } 740 741 /* if (!head->going) active = FALSE;*/ 742 743 g_mutex_unlock (s->play_mutex); 744 } 745 } 746 747 return; 748 } 749 750 /* how many inactive writes to do before closing */ 751 #define INACTIVE_TIMEOUT 256 752 753 static gboolean 754 monitor_active (void) 755 { 756 int * use_monitor; 757 758 use_monitor = prefs_get_int (USE_MONITOR_KEY); 759 760 if (use_monitor == NULL) return 0; 761 else return (*use_monitor != 0); 762 } 763 764 static void 765 play_active_heads (void) 766 { 767 sw_framecount_t count; 768 int inactive_writes = 0; 769 GList * gl; 770 sw_head * head; 771 sw_format * f; 772 int max_driver_chans = 0; 773 774 gboolean use_monitor; 775 776 #ifdef RECORD_DEMO_FILES 777 gchar * filename; 778 SNDFILE * sndfile = NULL; 779 SF_INFO sfinfo; 780 #endif 781 782 if (!active_main_heads && !active_monitor_heads) return; 783 784 use_monitor = (monitor_handle != NULL); 785 786 if (use_monitor) { 787 if ((gl = active_monitor_heads)) { 788 head = (sw_head *)gl->data; 789 f = head->sample->sounddata->format; 790 791 device_setup (monitor_handle, f); 792 } else if ((gl = active_main_heads)) { 793 head = (sw_head *)gl->data; 794 f = head->sample->sounddata->format; 795 796 device_setup (monitor_handle, f); 797 } 798 } 799 800 if ((gl = active_main_heads)) { 801 head = (sw_head *)gl->data; 802 f = head->sample->sounddata->format; 803 804 device_setup (main_handle, f); 805 806 #ifdef RECORD_DEMO_FILES 807 filename = generate_demo_filename (); 808 sfinfo.samplerate = f->rate; 809 sfinfo.channels = handle->driver_channels; 810 sfinfo.format = SF_FORMAT_AU | SF_FORMAT_FLOAT | SF_ENDIAN_CPU; 811 sndfile = sf_open (filename, SFM_WRITE, &sfinfo); 812 if (sndfile == NULL) sf_perror (NULL); 813 else printf ("Writing to %s\n", filename); 814 #endif 815 } else if ((gl = active_monitor_heads)) { 816 head = (sw_head *)gl->data; 817 f = head->sample->sounddata->format; 818 819 device_setup (main_handle, f); 820 } else { 821 return; 822 } 823 824 if (use_monitor) { 825 max_driver_chans = MAX (main_handle->driver_channels, 826 monitor_handle->driver_channels); 827 } else { 828 max_driver_chans = main_handle->driver_channels; 829 } 830 831 if (max_driver_chans > devbuf_chans) { 832 devbuf = g_realloc (devbuf, 833 PSIZ * max_driver_chans * sizeof (sw_audio_t)); 834 devbuf_chans = max_driver_chans; 835 } 836 837 while (!stop_all && inactive_writes < INACTIVE_TIMEOUT) { 838 839 if (active_main_heads == NULL && active_monitor_heads == NULL) { 840 inactive_writes++; 841 } else { 842 inactive_writes = 0; 843 } 844 845 g_mutex_lock (play_mutex); 846 if (use_monitor) { 847 prepare_to_play_heads (active_monitor_heads, monitor_handle); 848 prepare_to_play_heads (active_main_heads, main_handle); 849 } else { 850 prepare_to_play_heads (active_monitor_heads, main_handle); 851 prepare_to_play_heads (active_main_heads, main_handle); 852 } 853 g_mutex_unlock (play_mutex); 854 855 if (use_monitor) { 856 count = PSIZ * monitor_handle->driver_channels; 857 memset (devbuf, 0, count * sizeof (sw_audio_t)); 858 play_heads (&active_monitor_heads, monitor_handle); 859 device_write (monitor_handle, devbuf, count); 860 861 count = PSIZ * main_handle->driver_channels; 862 memset (devbuf, 0, count * sizeof (sw_audio_t)); 863 play_heads (&active_main_heads, main_handle); 864 device_write (main_handle, devbuf, count); 865 } else { 866 count = PSIZ * main_handle->driver_channels; 867 memset (devbuf, 0, count * sizeof (sw_audio_t)); 868 play_heads (&active_monitor_heads, main_handle); 869 play_heads (&active_main_heads, main_handle); 870 device_write (main_handle, devbuf, count); 871 } 872 873 #ifdef RECORD_DEMO_FILES 874 if (sndfile) 875 sf_writef_float (sndfile, devbuf, count / main_handle->driver_channels); 876 #endif 877 } 878 879 if (use_monitor) { 880 device_reset (monitor_handle); 881 device_close (monitor_handle); 882 } 883 884 device_reset (main_handle); 885 device_close (main_handle); 886 887 #ifdef RECORD_DEMO_FILES 888 if (sndfile) { 889 printf ("Closing %s\n", filename); 890 sf_close (sndfile); 891 } 892 #endif 893 894 player_thread = (pthread_t) -1; 895 } 896 897 static gboolean 898 ensure_playing (void) 899 { 900 sw_handle * h; 901 902 if (player_thread == (pthread_t) -1) { 903 if ((h = device_open (0, O_WRONLY)) != NULL) { 904 main_handle = h; 905 if (monitor_active()) { 906 monitor_handle = device_open (1, O_WRONLY); 907 } else { 908 monitor_handle = NULL; 909 } 910 911 pthread_create (&player_thread, NULL, (void *) (*play_active_heads), 912 NULL); 913 return TRUE; 914 } else { 915 return FALSE; 916 } 917 } 918 919 return TRUE; 920 } 921 922 void 923 sample_play (sw_sample * sample) 924 { 925 sw_head * head = sample->play_head; 926 927 play_head_update_device (head); 928 head_init_playback (sample); 929 930 head_set_going (head, TRUE); 931 932 sample_refresh_playmode (sample); 933 934 if (ensure_playing()) { 935 start_playmarker (sample); 936 } else { 937 head_set_going (head, FALSE); 938 sample_refresh_playmode (sample); 939 } 940 } 941 942 void 943 sample_update_device (sw_sample * sample) 944 { 945 sw_head * head = sample->play_head; 946 947 if (!head->going) return; 948 949 play_head_update_device (head); 950 } 951 952 void 953 play_view_all (sw_view * view) 954 { 955 sw_sample * s = view->sample; 956 sw_head * head = s->play_head; 957 958 sample_set_stop_offset (s); 959 sample_set_previewing (s, FALSE); 960 961 prev_sample = s; 962 963 head_set_restricted (head, FALSE); 964 965 sample_play (s); 966 } 967 968 void 969 play_view_sel (sw_view * view) 970 { 971 sw_sample * s = view->sample; 972 sw_head * head = s->play_head; 973 974 if (s->sounddata->sels == NULL) { 975 head_set_going (head, FALSE); 976 sample_refresh_playmode (s); 977 return; 978 } 979 980 sample_set_stop_offset (s); 981 sample_set_previewing (s, FALSE); 982 983 prev_sample = s; 984 985 head_set_restricted (head, TRUE); 986 987 sample_play (s); 988 } 989 990 void 991 play_preroll (sw_view * view) 992 { 993 sw_sample * s = view->sample; 994 sw_head * head = s->play_head; 995 996 sample_set_stop_offset (s); 997 sample_set_previewing (s, TRUE); 998 sample_set_scrubbing (s, FALSE); 999 1000 prev_sample = s; 1001 1002 head_set_restricted (head, FALSE); 1003 1004 sample_play (s); 1005 } 1006 1007 void 1008 play_preview_cut (sw_view * view) 1009 { 1010 sw_sample * s = view->sample; 1011 sw_head * head = s->play_head; 1012 1013 if (s->sounddata->sels == NULL) { 1014 head_set_going (head, FALSE); 1015 sample_refresh_playmode (s); 1016 return; 1017 } 1018 1019 sample_set_stop_offset (s); 1020 sample_set_previewing (s, TRUE); 1021 sample_set_scrubbing (s, FALSE); 1022 1023 prev_sample = s; 1024 1025 head_set_restricted (head, TRUE); 1026 1027 sample_play (s); 1028 } 1029 1030 void 1031 play_view_all_pitch (sw_view * view, gfloat pitch) 1032 { 1033 sw_sample * s = view->sample; 1034 sw_head * head = s->play_head; 1035 sw_framecount_t mouse_offset; 1036 1037 mouse_offset = 1038 sample_display_get_mouse_offset (SAMPLE_DISPLAY(view->display)); 1039 sample_set_playmarker (s, mouse_offset, TRUE); 1040 1041 sample_set_stop_offset (s); 1042 sample_set_previewing (s, FALSE); 1043 1044 prev_sample = s; 1045 1046 head_set_restricted (head, FALSE); 1047 head_set_rate (head, pitch); 1048 1049 sample_play (s); 1050 } 1051 1052 void 1053 stop_all_playback (void) 1054 { 1055 stop_all = TRUE; 1056 g_list_free (active_main_heads); 1057 active_main_heads = NULL; 1058 } 1059 1060 void 1061 pause_playback (sw_sample * s) 1062 { 1063 sw_head * head; 1064 1065 if (s == NULL) return; 1066 1067 head = s->play_head; 1068 1069 if (head->going) { 1070 head_set_going (head, FALSE); 1071 } 1072 1073 sample_set_stop_offset (s); 1074 } 1075 1076 void 1077 stop_playback (sw_sample * s) 1078 { 1079 sw_head * head; 1080 1081 if (s == NULL) return; 1082 1083 head = s->play_head; 1084 1085 if (head->going) { 1086 head_set_going (head, FALSE); 1087 sample_set_playmarker (s, head->stop_offset, TRUE); 1088 1089 g_mutex_lock (play_mutex); 1090 active_main_heads = g_list_remove (active_main_heads, head); 1091 g_mutex_unlock (play_mutex); 1092 } 1093 } 1094 1095 gboolean 1096 any_playing (void) 1097 { 1098 return ((player_thread != (pthread_t) -1) && (active_main_heads != NULL)); 1099 } 1100 1101 void 1102 init_playback (void) 1103 { 1104 play_mutex = g_mutex_new (); 1105 }
Here this is what sweep is
Take the screenshot tour for a visual introduction to Sweep. The full list of features includes the following: Extreme ease of use: Unlimited undo/redo with a fully revertible edit history Multithreaded background rendering and file import/export: effects processing is not disabled during playback, providing instantaneous feedback. Precise, interactive scrubbing Multichannel file support: Sweep provides complete editing and processing for multichannel sound files. Customisable keybindings available for all operations Translations for French, Hungarian, Italian, German, Russian and Polish. Support for many PCM and voice file formats via libsndfile by Erik de Castro Lopo: at least 14 PCM sound file formats formats including WAV, W64, AIFF/AIFF-C, IFF/SVX, AU and raw PCM files. PCM audio encoding formats including 8/16/24/32 bit PCM, 32 and 64 bit floating point, u-law, A-law and ADPCM voice encoding formats including GSM 6.10, G721/G723 ADPCM, 12/16/24 bit DWVW Support for Ogg Vorbis format audio files: an exteremely high quality, free, open and unpatented perceptual audio codec. supports intuitive variable bitrate encoding, where the Vorbis encoder makes low-level encoding decisions. provides access to average bitrate controls, including bounds on minimum and maximum bitrate usage. Sweep provides sensible defaults and remembers encoding options between sessions. Support for Speex files: a free, open and unpatented speech codec designed for Voice over IP (VoIP) and file-based compression. supports optional variable bitrate and control over encoding complexity and frame packing. Sweep provides sensible defaults and remembers encoding options between sessions. Support for LADSPA effects plugins, including: the Computer Music Toolkit by Richard Furse, containing high and low pass filters, compressors, delays, and a port of the popular Freeverb reverb unit. SWH plugins, over 30 effects plugins by Steve Harris, including overdrives, comb filter, ring modulator, pitch scaler, chorus, flanger and various distortions. more listed at ladspa.org Powerful editing operations: Cut, copy and paste insert: conventional cut or copy selected regions of a sound, and paste into the same or other sounds. Discontinuous selections: select multiple regions of a sound and operate on these regions simultaneously interactive moving and merging of selection boundaries Selection invert, selection double/halve and selection shift left/right Intuitive visualisation: Multiple views per clip Variable zooming to below 1:1 with mouse wheel support View Center, Zoom to Selection, Zoom Normal Easy differentiation of multiple files with 6 standard colour schemes including Decoder Red, Orangeboom, Coogee Bay Blue and Blackwattle. Versatile playback modes: Standard transport features: play/pause, stop, rewind, fast forward, scan reverse/forward, go to end/beginning. Simultaneous playback of multiple files Play all, play selection, and looping playback Interactive reverse playback: includes reverse previews and reverse looping Flexible recording: independent record and playback heads "tape loop" style recording reverse recording Device handling and portability: Support for Open Sound System compatible audio devices (eg. stock Linux kernel), ALSA 0.6.0 and Solaris audio. Intuitive control of application latency Support for both little and big endian CPU architectures Live playback "DJ" features: Two-handed interactive scrubbing Independent gain levels per file +/-10% pitch slider Piano-style sample playback
And the code I posted where ask if was said right it does work, and it is in the order that you wrote but it did not fix the problem so I thought it could be wrong.
It is is this order but it is in the ticker timer 0
while(1){ Wait for an audio packet being received Reverse the buffer Soften the buffer Playback buffer }
Philips Philips wrote:
And the code I posted where ask if was said right it does work, and it is in the order that you wrote but it did not fix the problem so I thought it could be wrong.
It is is this order but it is in the ticker timer 0
But the ticker is executed for every sample value isn't it ? The softening needs to be done only for the complete packet (so the start and end start at zero level)
Gert van der Knokke wrote:
Philips Philips wrote:
And the code I posted where ask if was said right it does work, and it is in the order that you wrote but it did not fix the problem so I thought it could be wrong.
It is is this order but it is in the ticker timer 0
But the ticker is executed for every sample value isn't it ? The softening needs to be done only for the complete packet (so the start and end start at zero level)
I did not know that is this right Gert?
// Hello World example for the USBMIDI library (circular buffer) #include "mbed.h" #include "USBAudio.h" #include "CircBuffer.h" Serial pc(USBTX, USBRX); // frequency: 48 kHz #define FREQ 32000 // 1 channel: mono #define NB_CHA 1 // length computed: each ms, we receive 48 * 16bits ->48 * 2 bytes. as there is one channel, the length will be 48 * 2 * 1 #define LENGTH_AUDIO_PACKET 32 * 2 * 1 #define USR_POWERDOWN (0x104) int semihost_powerdown() { uint32_t arg; return __semihost(USR_POWERDOWN, &arg); } // USBAudio USBAudio USBaudio(FREQ, NB_CHA, 0x7186, 0x7507); // speaker connected to the AnalogOut output. The audio stream received over USb will be sent to the speaker AnalogOut speaker(p18); // ticker to send data to the speaker at the good frequency Ticker tic; // circular buffer where will be stored data to be sent to the speaker CircBuffer<int16_t> cbuf(LENGTH_AUDIO_PACKET); // previous value sent to the speaker uint16_t p_val = 0; // function executed each 1/FREQ s void tic_handler() { int16_t val; float speaker_value; unsigned short PlaySample; if (cbuf.available() >= 1) { cbuf.dequeue(&val); speaker_value = (float)val; // speaker_value between 0 and 65535 speaker_value += 32768.0; // adjust according to current volume speaker_value *= USBaudio.getVolume(); } else { speaker_value = p_val; } p_val = speaker_value; /* ADAMGR: Inserting my May 30th reversing code here!!! */ { static const unsigned int BufferSize = 5 * 1024; static unsigned short Buffer[BufferSize]; // int samples[MAX_SAMPLES]; static int Index = 0; static int Direction = 1; static int Playback = false; static int ChunkSize = BufferSize; // float multiplier = 0.0; unsigned short ReadSample; /* Default PlaySample to the current sample from USB buffer. */ PlaySample = speaker_value; /* Read out the sample from the buffer to be played back */ if (Playback) { PlaySample = Buffer[Index]; } /* Obtain current audio sample from the USB buffer. */ ReadSample = (unsigned short)speaker_value; /* Record the sample into the buffer right where a space was freed up from the PlaySample read above */ Buffer[Index] = ReadSample; /* Increment the buffer pointer */ Index += Direction; /* Check to see if the chunk has been filled */ if (Index < 0) { /* Now have a chunk to be played back */ Playback = true; /* Reverse the direction of playback and recording */ Direction *= -1;//Direction; Index = 0; } else if (Index >= ChunkSize) { /* Now have a chunk to be played back */ Playback = true; /* Reverse the direction of playback and recording */ Direction *= -1;//Direction; Index = ChunkSize - 1; } } // send value to the speaker speaker.write_u16((uint16_t)PlaySample); } } int main() { int16_t buf[LENGTH_AUDIO_PACKET/2]; semihost_powerdown(); // attach a function executed each 1/FREQ s tic.attach_us(tic_handler, 1000000.0/(float)FREQ); while (1) { // read an audio packet USBaudio.read((uint8_t *)buf); int t = 0; // buffer for our reversed samples (I have used signed int as values) int MAX_SAMPLES = 3 * 1024; int sample[MAX_SAMPLES]; // the multiplier we are going to apply to the sample value float multiplier=0.0; MAX_SAMPLES = LENGTH_AUDIO_PACKET/2; for (t=0; t<512; t++){ // fade out end samples to zero sample[MAX_SAMPLES-t]=sample[MAX_SAMPLES-t]*multiplier; // fade in start samples from zero sample[t]=sample[t]*multiplier; // increment multiplier so we end up at 1 after 10 samples multiplier=multiplier+0.1; // put buffer in the circ buffer for(int i = 0; i < LENGTH_AUDIO_PACKET/2; i++) { cbuf.queue(buf[i]); } } } }
Gert,
I am kinda confused now, If 1 packet is received each ms and then unpacked and audio it sent every 31.25 us seconds, how am I supposed to send this 1 packet you talk of after it has the reverse effect applied in real time. when audio is sent and reversed every 31.25 us seconds, that would mean a 1000 cycles.
the way you are suggesting it I would need to delay my speaker output by 1ms, which is 1000us I would like to get not such a long delay if possible.
Philips Philips wrote:
I am kinda confused now, If 1 packet is received each ms and then unpacked and audio it sent every 31.25 us seconds, how am I supposed to send this 1 packet you talk of after it has the reverse effect applied in real time. when audio is sent and reversed every 31.25 us seconds, that would mean a 1000 cycles.
Without some buffering there is no way to know what the next sample value will be (it is realtime) and reversing will never be possible without clicks. Just because it is possible in an audio editor does not mean it is possible in realtime.. The fact you can do realtime scratching on for example a CDJ800 is only possible because it buffers a great deal of the CD track and it also spins at much higher speed than a normal (realtime) CD player. It already knows what samples are coming and also which samples have just been played (they are still in the buffer)
Quote:
the way you are suggesting it I would need to delay my speaker output by 1ms, which is 1000us I would like to get not such a long delay if possible.
Have you tried this already ? I know from audio/video sync problems that anything above 10 ms is quite noticable so maybe 1 ms is not so bad ? Another trick might be that if you are mixing reverse samples and the original sound you need to delay the original sound also so all you can hear are 1 ms delayed samples.
I know us microseconds is possible in sweep and with mbed, but it may need to be in assembly language, 1ms is not bad but I want to get lower if possible.
I think I need 3 timers;) first timer fills up buffer every 31.25us, second timer receives buffer every 31.25us and fills up a buffer and applys effect every 200us, 3 timer receives data from second timer and plays apply effected audio at 31.25us
I think I need help to do this:) I don't know how to transfer the data from timer to timer.
<<quote>>You may put PCM8 data (offset binary) directly to the DAC.<</quote>>
I still think that using 8bits and 8000khz should fix the clicking that would be 125us and only 0.255.
But what do I need to do to the usb audio demo http://mbed.org/handbook/USBAudio I am getting distorted sound at the moment, well sound you can not listen to because it offset. do I just need to make sure that all buffers are int8_t or unsigned 8 ????
Also I wonder for when using 16bit if I will have enough memory and if DMA should be used to transfer the data from the tickers???
It all depends on what kind of audio data you send to the mbed from the PC.
Is it 16 bit or is it 8 bit, is it signed or unsigned is it stereo or mono? Also in what order are 16 bits data received? High byte first or low byte first etc.
Does the example http://mbed.org/users/samux/programs/USBAUDIO_speaker/m1h5sc code work ok ?
Gert van der Knokke wrote:
It all depends on what kind of audio data you send to the mbed from the PC.
Is it 16 bit or is it 8 bit, is it signed or unsigned is it stereo or mono? Also in what order are 16 bits data received? High byte first or low byte first etc.
Does the example http://mbed.org/users/samux/programs/USBAUDIO_speaker/m1h5sc code work ok ?
I don't know what format the audio is because I am trying to watch videos off youtube and listen to radioshure internet radio stream.
But not matter what I chuck at it the usb speaker code works that samux coded http://mbed.org/users/samux/programs/USBAUDIO_speaker/m1h5sc
But when I try to make changes to it to make it work at 8bits it will not work even when I just use the FORMAT_PCM at 8bits, this is what made me try FORMAT_PCM8 but that will not work. You will have to look at the usbdevice libraries
http://mbed.org/users/samux/libraries/USBDevice/m1g6da
as far as I know the usb speaker code output mono
LSB(FORMAT_PCM), // wFormatTag MSB(FORMAT_PCM), // wFormatTag
Samuel Mokrani wrote:
The read() function fills an uint8_t array. But these data has to be interpreted as 16 bits signed data (PCM). Then PCM values can be handled according to the number of channels.
I would just use 8bits FORMAT_PCM if I could then it would still be PCM (0x0001) means 2's complementary binary data
instead of PCM8 (0x0002) means offset binary data
But I just want to get 8bits working.
also I see now other way to write to the DAC unsigned 8
<<code>>write Set the output voltage, specified as a percentage (float)<</code>>
<<code>>write_u16 Set the output voltage, represented as an unsigned short in the range [0x0, 0xFFFF<</code>>
Well here I changed USBAudio.cpp
// Audio Type I Format FORMAT_TYPE_I_DESCRIPTOR_LENGTH, // bLength INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType STREAMING_FORMAT_TYPE, // bDescriptorSubtype FORMAT_TYPE_I, // bFormatType channel_nb, // bNrChannels 0x01, // bSubFrameSize changed from 0x02 to 0x01 8, // bBitResolution changed from 16 to 8 0x01, // bSamFreqType LSB(FREQ), // tSamFreq (FREQ >> 8) & 0xff, // tSamFreq (FREQ >> 16) & 0xff, // tSamFreq
I Changed main.cpp
int8_t buf[AUDIO_LENGTH_PACKET]; // change from int16_t got rid of /2 //same as above uint8_t p_val = 0;
Changed
speaker.write((uint8_t)PlaySample); // from write.u16 uint16_t
change usbaudio.h
int8_t buf[AUDIO_LENGTH_PACKET]; for(int i = 0; i < AUDIO_LENGTH_PACKET; i++) {
Not sure if to change here
*/ virtual void USBCallback_requestCompleted(uint8_t * buf, uint16_t length);
is this right??? what else needs to be changed???
It is right when it sounds right, try taking one step at a time. So first get normal play working then concentrate on the reversal part.
8 bit unsigned (0-255, level zero=128) can be written as 16 bit to the dac after it has been shifted 8 positions to the left (or multiplied by 256)
8 bit signed (-127 to 127, level zero=0) must be converted first (add 128) and then shift 8 positions to the left again.
Sorry total disagree, this is my favourite design based around a SAR you can't beat SAR converter in my eyes. I guess you never try a Flash ADC Gert, but for my projects SAR converter are the best to use:)
bigger picture http://mbed.org/media/uploads/mbed2f/panasonic_dr_60_1.jpg
http://mbed.org/media/uploads/mbed2f/panasonic_dr_60_2.jpg
I think maybe I should have called myself panasonic instead:) of Philips:(