close-circle
Close
0%
0%

Automatic audio source switching

Automatic audio switching between my computer and TV to shared amplified speaker.

Similar projects worth following
This is an automatic source switch between my computer or TV source to my desktop amplifier. A mixer or a simple audio detection switch won't work as I sometimes watch short videos on my computer during commercial on TV. The switching is based on threshold detection and a state machine implemented on STM32F030F4. Also automaticly reduces volume of loud audio.

What I am trying to do:

  • I want a box that automate the switching audio source between my TV and computer as I am manually doing that a few times a day with a DPDT switch.
  • A lot of times, obnoxious installed computer games intro or youtube or unexpected webpage advertising popup volume is set too high. I would like to reduce the audio level automatically.
  • I sometimes watches video clips on my computer during a TV commercial break, so the audio source needs to be switched to the computer and back to the TV when I am done. A mixer solution won't work here.
  • The TV doubles as my 2nd monitor. By also connecting the audio that runs through a different cable,I have created a "ground loop". Right now, I also have a SPDT switch to select the ground connections for certain peripherals that causes noise, but this is not ideal.

The ground loop becomes a big antenna for magnetically picking up noise within the loop area. One way to break the ground loop is to use balanced signals for the audio.

Block diagram

Differential Amplifier

The differential amplifiers helps to remove the Common Mode noise even for a quasi-balanced with the ground acting as the "cold" wire. By breaking the ground connections, this also help to remove the ground loop. Initially, I was going to place the differential amp after the mux. I realize that the mux I have are very old and their high R(on) resistance is going to strongly reduce the common mode rejection ratio. (see here for explanation) So I am better off with more amplifiers.

Bipolar low noise/distrortion opamp e.g. El Cheapo NE5532 (30+ years old!) are good enough for this project, unfortunately they require +/-5V rails.

Analog Mux

A dual 4:1 Analog Mux is used for switching the audio signals. Voltage dividers are used to provide an attenuated version of the input signals when tne firmware detected loud audio input.

ADC

The ADC in the ARM is used to continuously scan the audio input from the two audio sources to detect active audio source. The data can also be used for optional visualization such as a Spectrum Analyzer, VU meter etc.

A simple voltage divider is used to attenuate and bias the bipolar audio signal to the 0-3.3V range. This extends the ADC range to +/- 3.3V which is about 2.33V RMS for a pure sine wave. The Green trace is the audio input and the Blue one is what the ADC sees.

1.8nF capacitors are added to the input. They act as anti-alias filter and lower AC source impedance for the sampling ADC.

The -3dB point for the filter is around 18kHz. ADC sampling rate is set to 38.4k samples/s which has a Nyquist frequency of fs/0.5 = 19.2kHz.

Volume Meter

Basically, a VU meter is a full wave rectifier driving a moving coil meter with a capacitor for averaging. The capacitor sets the rise time to 300 miliseconds. The long time constant averaged values reflects the volume of the audio input. 1.228V RMS is defined as 0VU. This is also the audio output level from my PC.

The summing and rectifier is implemented in firmware. The pseudo code is as below.

Vol_R = Vol_L = 0;  
Raw = ADC_Data.AudioBuffer;

for(i=0;i<size;i+=2)         
{
  Volume = *Raw++ -Offset_R;
  Vol_R+= (Volume>=0)?Volume:-Volume;
  Volume = *Raw++ -Offset_L;
  Vol_L+= (Volume>=0)?Volume:-Volume;           
}

Vol_R/= ADC_AUDIO_SAMPLES;
Vol_L/= ADC_AUDIO_SAMPLES;

Offset_L, Offset_R are the input offset. They are computed by taking an average of the input sample. Any values above it is a positive voltage, so the offset is subtracted off. Voltages below that are negative, so the difference is used. Thus the loop adds up the absolute values.

This produce an average for a sample period at a point in time. This is used to detect the present of audio. I used a IIR filter for averaging the values over time which is used to drive a log scale bar graph on the LCD.

Bar Graph Display

The following piece of code is used for log scale conversion and output rendering. dB_Table stores the threshold value for dBV.

for(i=0;i<dB_TBL_ENTERIES;i++)
  {
...
Read more »

  • 2 × NE5532 Amplifier and Linear ICs / Operational Amplifiers
  • 1 × 74HC4052 Switches and Multiplexers / Analog Switches and Multiplexers
  • 1 × STM32F030F4P6 ARM, RISC-Based Microcontrollers

  • Youtube demo - glitchless switching

    K.C. Lee09/01/2016 at 04:29 0 comments

    This time it is with the daughtercard doing the switching. The audio switching is now glitch free. (The clicking noise is from my mouse button.)


    I have added the loudness protection. The code is on github. The major features seem to be working well enough for my daily use.

    Here are some scope captures of the loudness protection in action. It takes a few samples for the volume averaging to reach the threshold. Once the loudness threshold is crossed, the output signal is attenuated by a voltage divider and a timeout counter that gets reloaded. When the timer goes to zero, the loudness protection is reset. This behaviour is controlled by the firmware, so it can be tweaked.

    This is the "Chiptune" background music for a trainer that is a bit too loud.

    This is from a movie. The loudness protection kicks in right at the first narration.

  • Daughtercard assembly

    K.C. Lee08/29/2016 at 20:53 3 comments

    This daughtercard finally showed up this afternoon. It seems to have the least luck so far.

    PCB can be ordered: https://oshpark.com/shared_projects/nYDy556B

    I guess there is a discrepancy on the two PCB. The layout of the main PCB was updated shortly after I found out that the audio connectors were flipped. In updating the PCB, I have decided to align the grid of the daughtercard connector to a 0.1" grid. Both PCB for the main and daughter cards and the 3D models released on github are the updated version with the correct spacing.

    The daughtercard connector spacing is slightly wider than my main board prototype.

    Here is how I make up for the wrong spacing for my main card. I use a bottom entry connector as their pins are 0.3" apart to accommodate the pins coming from the solder side of the PCB. I cut the dual row connector into a single row and bend the pins slightly for the right spacing.

    This is how it will fit.

    A bit of SMT soldering/ hot air reflow later.

    The connector partially covers the resistors. It is actually not touching the resistors as they left a cavity in the connector for the pins.

    This is how it look when the PCB are fitted together.

    Errata:
    • ☑ connector spacing on my prototype is incorrect. Already been fixed.
    • ☑ Filter changed to 0805 600R ferrites as the I*R drop on the +/-5V rail is too high with the RC filter.
    • ☑ Seems to be loud hums in the circuit. This is because the boards are floating and do not share ground with either the PC or the TV.

    I connected the ground of one of the TV audio inputs to the output ground with the alligator clip and now the hum is gone!

    Looks like the loud click with the relay is also gone with the analog MUX! I haven't even implemented the code to minimize the glitch. Probably the glitch frequency is beyond the frequency response of the amplifier. :)

    The SWD cable clears the daughter card as expected.

    I made a modification on the PCB for the ground connection. I think at this point the hardware is considered as final with the exception of the loudness voltage divider values that I might tweak. I have uploaded an update on github.


    I use 10K 1% resistors (blue). I sorted a batch of these resistors so that they are matched to 4 digits on my multimeter (i.e. within 10 ohms or within 0.1%)

    Each of the following coloured groups of 4 resistors use resistors of the same values.

  • Glitchless audio switching

    K.C. Lee08/24/2016 at 00:50 0 comments

    While I am waiting for the PCB to show up, I have been thinking about how to implement glitchless switching.

    To minimize glitches during switching, the input voltages of both pairs of audio sources can be switched when they are within a threshold.

    STM32F030's analog watchdog cannot be used here as it can only cause an IRQ when the ADC values are outside a range and not inside. The switching can be done in firmware inside the ADC DMA IRQ after it collects a sample of all the analog channels. IRQ rate is every 25us which is well within what the STM32F030 can handle. An analog MUX is used for switching the source as it switches with minimum delay (~50ns) and without any contact bounces.

  • Stand-in daughtercard

    K.C. Lee08/19/2016 at 03:14 2 comments

    Update: Proof of concept switching with the relays: It works!

    Relay switching is loud and I haven't fine tuned delays etc., but the automatic switching concept works! Actually the loud pop and relay noises let you know when the switching happen.

    Left side: TV, right side: PC on youtube. (replaced video - last one was too loud. fixed)


    If either PC or TV audio is active, the switch select that audio source. The PC side is given priority so that you can interrupt mundane TV by watching video/play music/games etc.

    The noise relay problems should be eliminated when the proper audio switch daughtercard shows up.

    Been watching TV, youtube video, playing games etc for the last few hours automatically switching the input to the amplifier doing what I expect it to do. :)

    One thing I found is that if the PC video has a lot of silence pauses (without background noise/music) which exceeds the time-out parameter and switch back to the TV. The easy fix is to mute the TV with the remote. That is not too much of an inconvenience.


    The daughter card PCB won't be here for another 2+ weeks due to on-going shipping issues out of my control. Here is a big mess of wire version of the daughtercard implemented with mechanical relays. A 2N2222 transistor is used to drive the relays. One of the relays switches the ground while the other one switches the L/R channels.

    Relay contacts bounce during switching and inject noise into the audio. That's why I didn't use them for my design.

  • Github source "Lost in Space" release

    K.C. Lee08/17/2016 at 18:43 5 comments

    While the folks at OSH Park have been great helping me, I have lost 4-5 weeks just waiting for packages that never show up. UPS innovation, USPS, Canada Custom, Canada Post all have their fingers in the pie. Not sure if/when this will be fixed as this is the 3rd shipment in a row that hasn't show up or extremely late.

    I have decided to pre-release my firmware on github. The code is hack together and haven't gone through polishing and clean ups. On the other hand, it has been running for days now. There are a few loose end that cannot be tested without the daughter card.

    https://github.com/FPGA-Computer/Automatic-Audio-Switch

    Judging from the time my other package has been sitting at USPS, it would take another 2 weeks or so before it would show up probably end of the month. Can't blame Canada for being the slow poke here. They upgraded my last few packages inside Canada to Express Post and which took another 2 days or so to show up.

    Chinese PCB shipping take similar time frame.

    So between that and my lost shipment, my schedule has gone down the toilet. Thankfully, I etch my main PCB and got something to show for.

    Meanwhile the package is still sitting there and not expected to move until mid week next week. All that delay likely going to run into a much bigger one - Canada Post job action could start as early as next Monday.

    This is as far as tracking would go as Canada Post uses a different tracking#. Let's hope that the job action isn't a full strike and doesn't affect 2 days package delivery. Likely delivery day is next Monday.

    Here is the Canada Post tracking. Kind of braindead as I don't have the tracking# until after receiving it.


    Here is the order that was lost. It spent 2 months between UPS and USPS and I have finally received it today.

  • Control Logic - State machine

    K.C. Lee08/01/2016 at 04:01 0 comments

      While I am waiting for the PCB to come back, here is what I have in mind. This is a state machine for the switching logic that determines which of the input to the amplifier is selected. The volume levels from both sources are continuously sampled by the ARM every 3.33ms.

      1. When there is only one active audio source, then the audio is switched to that source.
      2. The PC side is given a high priority, so it can interrupt audio from the TV. I like to watch something else on my PC while the long commercial breaks.
      3. If the source is loud prior to switching, then the volume is attenuated by using a voltage divider.
      4. If the loud source has became quiet, then the volume is switched back to normal.
        (I might try to implement some hysteresis to go between 3 & 4?)

      I might have to play around with additional timeouts etc. These are the kind of things that I have to tweak with the analog daughter PCB arrives and assembled.

      I have deviated from the state machine some what. Here is what I use and it seems to be working well so far.

      // State machine for switching
      switch(Audio_Data.State)
      {
        case Audio_Idle:
        case Audio_TV_Norm:
        case Audio_TV_Loud:
          if(AUDIO_LOUDNESS_CH(Source_PC)==Audio_Loud)
            Audio_Data.State = Audio_PC_Loud;
          else if (AUDIO_LOUDNESS_CH(Source_PC)==Audio_Detect)
            Audio_Data.State = Audio_PC_Norm;
          else if (AUDIO_LOUDNESS_CH(Source_TV)==Audio_Loud)
            Audio_Data.State = Audio_TV_Loud;
          else if (AUDIO_LOUDNESS_CH(Source_TV)==Audio_Detect)
            Audio_Data.State = Audio_TV_Norm;
          break;
      			
        case Audio_PC_Norm:
          if(AUDIO_LOUDNESS_CH(Source_PC)==Audio_Loud)
            Audio_Data.State = Audio_PC_Loud;
          else if (AUDIO_LOUDNESS_CH(Source_PC)==Audio_Detect)
            Audio_Data.State = Audio_PC_Norm;
          else if (AUDIO_LOUDNESS_CH(Source_PC)==Audio_None)			
            Audio_Data.State = Audio_Idle;
        break;
      }
      // State -> MUX setting
      GPIOA->BSRR = State_IO[Audio_Data.State];
      This state machine is called at the end of each block of audio data processed when the volume is calculated and compared against thresholds. The code also maintains timeout counters to allow for moments of silences in the audio source to prevent sputtering.

      The loudness control can't be tested as the PCB hasn't arrived. Getting the parameters for that part could be tricky.

  • NEC IR Remote Protocol

    K.C. Lee07/29/2016 at 12:39 0 comments

    After a lot of procrastination, I have settled on the IR Remote protocol and device to be used for this project. The tricky part is to find one that I don't have. :P I use my hacker friendly "All for One" URC-8017 Universal remote control to program in a new device.

    NEC protocol is popular and the clear winner as the standard allows for an 8-bit address and 8-bit command. Four bytes of data are transmitted. There are 2 for the address field and 2 for the command. The second in the pair is the one's complement. There is a newer "NEC2" extension which uses a similar format, but both bytes for a 16-bit address. This is the protocol used in my TV.

    I use timer 14 and Input Capture with both rising and falling edge triggered IRQ to measure the pulse width. The IRQ code reads off the current logic level of the pin from the GPIO port IDR (Input Data Register) and figure which edge it is at.

    Inside the IRQ service routine is a state machine that checks the pulse widths and keep tracks of the bit number. The code allows for timing variation such as crystal/resonator tolerances, rise/fall time of IR receiver on weak signal etc. The received data is checked against the complement field(s) and buffered in a FIFO queue.

    The code needs some more polishing and it is going to take a bit of work to make a user interface and a way of storing the setting for this project. Until then, I don't have an easy way to set the time on the RTC. That's why the time is always unset in the screenshots and demos.

    I found a couple of obscure bugs with my existing code. One of them was a race condition for a new DMA polling loop. The new code puts the ARM core into sleep mode while waiting for the LCD DMA to finish.

    void DMA1_Channel2_3_IRQHandler(void)
    {
       if(DMA1->ISR & DMA_ISR_TCIF3)
       {	
          SPI_Wait = 0;
          DMA1->IFCR = DMA_IFCR_CTCIF3;
        }
    }
    
    void SPI_DMA_Wait(void)
    {
      while(SPI_Wait)
       __WFI();
      
      // wait until SPI finishes
      while(SPI1->SR & SPI_SR_BSY)
        /* wait */ ;
    }
    
    void SPI_Block_Write(const uint8_t *ptr, uint16_t size)
    {
      DMA1_Channel3->CCR &= ~DMA_CCR_EN;
      DMA1_Channel3->CMAR = (uint32_t) ptr;
      DMA1_Channel3->CNDTR = size;
      SPI_Wait = 1;
        
      // 8-bit memory/peripheral, memory increment, write, enable, complete IRQ
      DMA1_Channel3->CCR = SPI_DMA_PRIORITY|DMA_CCR_MINC|DMA_CCR_DIR|DMA_CCR_TCIE|DMA_CCR_EN;
      SPI_DMA_Wait();  
    }
    

    Because the firmware have multiple IRQ, a shared flag variable, SPI_Wait, from the DMA IRQ service routine is to indicate that the DMA is done. The SPI_Wait was set in SPI_DMA_Wait() previously. Once in a while, DMA managed to fill the SPI FIFO and triggered the IRQ before SPI_DMA_Wait() get called. Thanks to the debugger, I found the issue. Now I set SPI_Wait prior to enabling the DMA.

    5 hours later and it still works, so I guess this is fixed.

    The other bug was the array indexing for plotting the spectrum was out of bound. One of the #define symbols was incorrectly used to find the upper bound of the rendering loop. The new code uses more RAM, so that memory index is now pointing at non-zeroed and it showed up in the plot as a spike.

  • Youtube Spectrum Analyzer (with Music) Demo

    K.C. Lee07/17/2016 at 18:35 0 comments

    Here is a demo of the Spectrum Analyzer on youtube. (~1: 48)

    This was recorded from my camera with the music redub from downloaded source / re-levelled.
    (My camera's mono audio isn't that great.)

    Music: "The Day I Die Remastered" by TeknoAXE
    Music License: Creative Commons Attribution 4.0 International
    URL: http://teknoaxe.com/Link_Code_3.php?q=812

  • Analog Daughtcard layout

    K.C. Lee07/12/2016 at 00:42 0 comments

    Here is how it looks so far. This is the analog daughter that houses the Balanced input to Unbalanced + MUX. The last minute voltage dividers for implementing the loudness protection really makes the routing difficult.

    Looks like for the very short term, Canada Post would try to keep their doors open. I am trying to put the finish touches on the layout and hope to order purple PCB some time tomorrow. I'll do some additional clean up before ordering. Judging from last time, it'll take about 3 weeks assuming the mail delivery is still going.

    My Aliexpress order is close to its 4 weeks mark. I am hoping that it would make it soon, but the delivery time have been around the 2 months time frame since beginning this year. Most of the delay is at Vancouver where the packages from China passes through Canada Custom. I really feels like I am in some 3rd world country.


    I guess I didn't procrastinate enough as I found a wiring mistake after ordering the PCB. Basically, I didn't check the voltage divider circuit and a=cross wired the L/R channels from different sources.

    Thanks to Jenner at OSH Park, I have cancelled the old one and resubmitted.

    Still not picked up by UPS after sitting for more than a week now. Meanwhile my new PCB that was sipped a week later was picked up yesterday (Thursday) and started its journey. UPS picks up these packages once a week.

    That PCB is a bit of unknow at the moment. I can only take the email at face value. I expect that packages are scanned in multiple times at different locations, so missing one shouldn't mean a complete blackout. They'll order a replacement for me if it doesn't show up early next week, but it'll take another 2-3 weeks to show up. :(


    Opened up another ticket and get the PCB ordered a 3rd time. This PCB just doesn't want to be made. They have upgraded the PCB to a swift run.

    My other PCB: here has not been updated since 28th July. ~3 week and it still hasn't crossed the border. This is worse than having stuff shipped from China.

    OSHW Park PCB shipping is completely broken for me. Looks like it won't show up before end of Aug at this rate.


    My Aliexpress order showed up just now. That only took 4 weeks which is back to the delivery time around busy Dec last year. This is better than the 8 weeks for my earlier orders this year. These days you have to have some level of ESP to order stuff well ahead of time to work with long lead time.

    I got some TI NE5532, some HC4052 MUX and some buttons for this project. I now have the parts for the daughter card.

    There are also the speculative inductors and LED boost controller I ordered which are going to be used for my Alarm project.

    I ordered some STM32F030 over the weekend. I have already used 8 of them out of 20! At $0.44 a piece, they are around the jelly bean logic chips prices so I have been using them for the last half dozen projects here.

  • Real Time Spectrum Plot

    K.C. Lee07/11/2016 at 04:43 0 comments

    Here is what a spectrum plot looks like.

    There are still some rough edges as this is the first working code. This is purely eye candy visualization for this project, but most of the signal processing code can be reused for something else.

    This is a neat one. The old fashion phone ringing in "The Matrix" movie.

    The X axis is linear from 300Hz to 12.6kHz with each colum correspond to a 300Hz "bin". 38.2ksps/(128/2) = 300Hz. The Y axis is log scale. I probably going to fool around with the scaling a bit.

    To test the FFT, I am using Audacity to generate a "Chirp" - a linear frequency sweep between 20Hz to 12kHz. It seems to be working. There seems to be some bleeding through from DC to the first bin in the plot.

    Aliasing starts at around 13kHz. The anti-alias filter -3dB point is around 14kHz, so the amplitude is a bit lower, but there is only so much a passive first order RC filter can do.

    This shows the amount of processor cycles needed. The plotting has very little code and it is limited by the LCD SPI clock speed at 4MHz. The LCD data transfer is done by DMA. There is still 490us left before the next DMA ADC block arrives.

    I overclock the LCD (from 3MHz) to 6MHz and it still takes quite a bit of time. There is now 1.285ms until the next block of data arrives.

    The LCD frame rate can be driven as high as 300/sec to keep up with the data rate, but are limited to what the display can show clearly.


    II have decided to add a minimalistic VU on top as most of the time that screen real estate is not being used. The VU meter will be overwritten by the Spectrum plot when needed.

    This has minimal impact on CPU usage.

    The LCD plastic sheet finally fell off, so I glue on a piece of clear plastic from a dollar store picture frame with a strip of double sided tape.


    I move th VU bargraph to the right hand side to make it more consistent. The spectrum plot is from 600Hz to 19.2kHz. There is a bit of leakage of the DC level into the 300Hz, so I have decided to not to plot that frequency bin..

    For demonstration, I generated a linear frequency sweep using the "chirp" generator in Audacity.

    Here is the animate gif for the spectrum plot as the Sine wave frequency increases linearly. The anti-alias filter attenuation the peak as the frequency increases towards the right hand side. Looks like the aliasing issues I have seen earlier might have been an artefact of the firmware.

    The lightning bolt is the loudness detection icon.

View all 17 project logs

Enjoy this project?

Share

Discussions

electrobob wrote 07/17/2016 at 20:17 point

You will need to get yourself an old fashion audio isolator based on a transformer. Putting a capacitor in the ground will not help, since the 50/60Hz signal on the ground is in the audio band. 

  Are you sure? yes | no

K.C. Lee wrote 07/17/2016 at 20:31 point

The Cap setup is for RF - i.e. cable TV.  That's not what I have used.

I had grounded the splitter right at the power bar Earth input to the home entertainment setup.  I have cancelled cable TV 10 years ago.  

I have built a SPDIF coax to optical box for connecting my PC up with the receiver, so there are no ground loops.

My current design is based on Balanced Inputs for audio source that shares the same power bar.

  Are you sure? yes | no

K.C. Lee wrote 06/08/2016 at 13:06 point

For what I am building, I am only dealing with less than 1V difference because all the stuff are on my desktop.  A simple differential amp should be able to work.  I was thinking about optical out and muxing, but that's way overkill for the computer speakers from the 1980's ("upgraded" with cheap $0.30 Class D amplifier from China) sitting behind my monitors.

The ground on a cable system is somewhere out there on a hydro post or in my case apartment wall 100 feet away on a different electrical system.   Few tens of volts different between the grounds is pretty much what I would expect.  I think there are dongles that I can buy with AC coupled the cable system ground for breaking ground loops.  They use low capacitance values that blocks low frequencies, but let the RF signal through.  I was impress that the local Radio Shack (long time ago) guy I talked to get the concept even though they don't carry it.

I had a power bar Earth ground connected to the ground connection of the cable splitter at my "entertainment console".  This way at least the ground point is at the power bar for the system. Most of the consumer electronics make it a point of not connecting to Earth ground and float everything inside but use single ended signals. Computers etc all have Earth ground connections, so things can get messed up when you are trying to connect them together. That seem to help a bit with the hums when I hook up my computer to the TV in the living room.  I later went with a receiver with optical inputs and built a bank of coax SPDIF to optical transmitter and used the xbox for streaming, so no more hums.

I became a cord cutter as cable TV here is meh, so the cable is only connected to the modem and the surrounding island of network stuff powered from my "Emergency Backup Power and Wallwart Eliminator".  Things exiting have the usual Ethernet 1000V isolation.

  Are you sure? yes | no

esot.eric wrote 06/08/2016 at 08:12 point

always planned on doing something like this... And ground-loops are a b**ch. Though, suddenly it occurs to me, what if both the ground-side and the signal-side were AC-Coupled (at the source?), and twisted-pair... maybe an opto-coupler... Hmm... The best was when my cable-box apparently tied to its own grounding-rod, kept shocking me... Turns out it was measuring 60V to the ground in the house. I never did figure out how to decouple that ground...

  Are you sure? yes | no

K.C. Lee wrote 06/08/2016 at 13:07 point

http://fillippa.dyndns.org/ce/reviews/Isolator.html

You want to ground that cable box to earth ground to avoid shocks.

  Are you sure? yes | no

esot.eric wrote 06/08/2016 at 13:19 point

Hah, there it is... both-sides AC-coupled... Shoulda thought of it years ago :/

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates